package labox.innovation.gameserver.model.actor;

import static labox.innovation.gameserver.ai.CtrlIntention.AI_INTENTION_ACTIVE;

import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javolution.util.FastList;
import javolution.util.FastMap;
import labox.innovation.config.Config;
import labox.innovation.gameserver.GameTimeController;
import labox.innovation.gameserver.ai.CtrlEvent;
import labox.innovation.gameserver.ai.CtrlIntention;
import labox.innovation.gameserver.ai.L2CharacterAI;
import labox.innovation.gameserver.datatables.SkillTable;
import labox.innovation.gameserver.handler.ISkillHandler;
import labox.innovation.gameserver.handler.SkillHandler;
import labox.innovation.gameserver.instancemanager.InstanceManager;
import labox.innovation.gameserver.model.ChanceSkillList;
import labox.innovation.gameserver.model.CharEffectList;
import labox.innovation.gameserver.model.IChanceSkillTrigger;
import labox.innovation.gameserver.model.L2Effect;
import labox.innovation.gameserver.model.L2ItemInstance;
import labox.innovation.gameserver.model.L2Object;
import labox.innovation.gameserver.model.L2Party;
import labox.innovation.gameserver.model.L2Skill;
import labox.innovation.gameserver.model.Location;
import labox.innovation.gameserver.model.Scene;
import labox.innovation.gameserver.model.SceneRegion;
import labox.innovation.gameserver.model.actor.instance.FuzePcInstance;
import labox.innovation.gameserver.model.actor.knownlist.CharKnownList;
import labox.innovation.gameserver.model.actor.position.CharPosition;
import labox.innovation.gameserver.model.actor.stat.CharStat;
import labox.innovation.gameserver.model.actor.status.CharStatus;
import labox.innovation.gameserver.model.itemcontainer.Inventory;
import labox.innovation.gameserver.model.listener.FuzeListenCharacterStatChange;
import labox.innovation.gameserver.model.listener.FuzeListenCharacterStatChange.ListenArg;
import labox.innovation.gameserver.model.quest.Quest;
import labox.innovation.gameserver.network.SystemMessageId;
import labox.innovation.gameserver.network.serverpackets.ActionFailed;
import labox.innovation.gameserver.network.serverpackets.Attack;
import labox.innovation.gameserver.network.serverpackets.L2GameServerPacket;
import labox.innovation.gameserver.network.serverpackets.MagicSkillLaunched;
import labox.innovation.gameserver.network.serverpackets.MagicSkillUse;
import labox.innovation.gameserver.network.serverpackets.Revive;
import labox.innovation.gameserver.network.serverpackets.StatusUpdate;
import labox.innovation.gameserver.network.serverpackets.SystemMessage;
import labox.innovation.gameserver.pathfinding.AbstractNodeLoc;
import labox.innovation.gameserver.pathfinding.Node;
import labox.innovation.gameserver.pathfinding.PathFinding;
import labox.innovation.gameserver.skills.AbnormalEffect;
import labox.innovation.gameserver.skills.Calculator;
import labox.innovation.gameserver.skills.Formulas;
import labox.innovation.gameserver.skills.Stats;
import labox.innovation.gameserver.skills.effects.EffectChanceSkillTrigger;
import labox.innovation.gameserver.skills.effects.EffectCountBuff;
import labox.innovation.gameserver.skills.funcs.Func;
import labox.innovation.gameserver.skills.l2skills.L2SkillAgathion;
import labox.innovation.gameserver.skills.l2skills.L2SkillMount;
import labox.innovation.gameserver.templates.chars.CharTemplate;
import labox.innovation.gameserver.templates.chars.NpcTemplate;
import labox.innovation.gameserver.templates.item.L2Weapon;
import labox.innovation.gameserver.templates.skills.L2EffectType;
import labox.innovation.gameserver.templates.skills.L2SkillType;
import labox.innovation.gameserver.timer.Timer;
import labox.innovation.gameserver.util.Util;
import labox.innovation.util.Rnd;

/**
 * 游戏世界中所有角色的父类，子类是(PC, NPC...)等<BR>
 * <BR>
 * 
 * L2Character :<BR>
 * <BR>
 * <li>L2CastleGuardInstance</li> <li>L2NpcInstance</li> <li>L2PlayableInstance</li><BR>
 * <BR>
 * 
 * 
 * <B><U> Concept of L2CharTemplate</U> :</B><BR>
 * <BR>
 * Each L2Character owns generic and static properties (ex : all Keltir have the same number of HP...). All of those properties are stored in a different template for each type of L2Character. Each template is loaded once in the server cache memory (reduce memory use). When a new instance of L2Character is spawned, server just create a link between the instance and the template. This link is stored in <B>_template</B><BR>
 * <BR>
 * 
 * 
 * @version $Revision: 1.53.2.45.2.34 $ $Date: 2005/04/11 10:06:08 $
 */
public abstract class L2Character extends L2Object {

	protected static final Logger _log = LoggerFactory.getLogger(L2Character.class.getName());

	private Timer _PvPRegTask;

	private long _pvpFlagLasts;

	// =========================================================
	// Data Field
	private L2Skill _lastSkillCast;

	public static final int FLAG_AFRAID = 0x0001;// Flee in a random direction

	public static final int FLAG_CONFUSED = 0x0002;// Attack anyone randomly

	public static final int FLAG_MUTED = 0x0004; // Cannot use magic

	public static final int FLAG_PHYSICALMUTED = 0x0008;// Cannot use physical skills

	public static final int FLAG_PHYSICALATTACKMUTED = 0x0010;

	public static final int FLAG_DEAD = 0x0020;

	public static final int FLAG_IMMOBILIZED = 0x0040;

	public static final int FLAG_SILENTMOVING = 0x0080;// Silent Move

	public static final int FLAG_OVERLOADED = 0x0100;// the char is carrying too much

	public static final int FLAG_PARALYZED = 0x0200;

	public static final int FLAG_DISARMED = 0x0400;

	public static final int FLAG_PENDINGREVIVE = 0x0800;//等待复活标记

	public static final int FLAG_REVIVEFULLRESUME = 0x1000;

	public static final int FLAG_ROOTED = 0x2000;// Cannot move until root timed out

	public static final int FLAG_IMMOBILEUNTILATTACKED = 0x4000; // Is in immobile until attacked.

	public static final int FLAG_SLEEPING = 0x8000;// Cannot move/attack until sleep timed out or monster is attacked

	public static final int FLAG_STUNNED = 0x10000;// Cannot move/attack until stun timed out

	public static final int FLAG_BETRAYED = 0x20000;// Betrayed by own summon

	public static final int FLAG_TELEPORING = 0x40000;//场景跳转标志

	public static final int FLAG_INVUL = 0x80000;//无敌标志

	public static final int FLAG_ALLSKILLSDISABLED = 0x100000;//所有技能被禁止的标记

	public static final int FLAG_AIDISABLED = 0x200000;//AI禁用标志

	protected int _flag;

	private L2Character _lastBuffer;

	/**
	 * 是否团队型标志怪物标志
	 */
	private boolean _isRaid;

	/**
	 * 是否团队型怪物中的仆从标志
	 */
	private boolean _isMinion;

	protected L2Effect _invulEffect;

	private int _lastHealAmount;

	/**
	 * 角色状态数值修正对象
	 */
	protected CharStat _stat;

	/**
	 * 角色HP/MP自恢复处理对象
	 */
	protected CharStatus _status;

	protected CharTemplate _template; // The link on the L2CharTemplate object containing generic and static properties of this L2Character type (ex : Max HP, Speed...)

	private String _title;

	/** Table of Calculators containing all used calculator */
	private Calculator[] _calculators;

	/** FastMap(Integer, L2Skill) containing all skills of the L2Character */
	/***
	 * 保存角色的 可用技能 _skills 最开始 初始化来自数据库
	 */
	private final Map<Integer, L2Skill> _skills;

	/**
	 * 状态改变时，可能需要通知某些对象 比如，当吸收了伤害值等
	 */
	private List<FuzeListenCharacterStatChange> _statListens;

	/**
	 * 在角色身上 注册一个监听器
	 * 
	 * @param statListen
	 */
	public void registStatListen(FuzeListenCharacterStatChange statListen) {
		if (_statListens == null) {
			_statListens = FastList.newInstance();
		}
		_statListens.add(statListen);
	}

	/**
	 * 移除角色身上的一个监听器
	 * 
	 * @param statListen
	 */
	public void removeStatListen(FuzeListenCharacterStatChange statListen) {
		if (_statListens != null) {
			_statListens.remove(statListen);
		}
	}

	/**
	 * 通知监听器状态变化了
	 * 
	 * @param arg
	 */
	public void notify(FuzeListenCharacterStatChange.ListenArg arg) {
		if (_statListens == null) {
			return;
		}
		for (FuzeListenCharacterStatChange statListen : _statListens) {
			statListen.notify(arg);
		}
	}

	/**
	 * 由Fastmap实现的一个包括所有激活的概率性发送的skill或effect
	 */
	protected ChanceSkillList _chanceSkills;

	/** Zone system */
	public static final byte ZONE_PVP = 0;

	public static final byte ZONE_PEACE = 1;

	public static final byte ZONE_SIEGE = 2;

	public static final byte ZONE_MOTHERTREE = 3;

	public static final byte ZONE_CLANHALL = 4;

	public static final byte ZONE_LANDING = 5;

	public static final byte ZONE_NOLANDING = 6;

	public static final byte ZONE_WATER = 7;

	public static final byte ZONE_JAIL = 8;

	public static final byte ZONE_MONSTERTRACK = 9;

	public static final byte ZONE_CASTLE = 10;

	public static final byte ZONE_SWAMP = 11;

	public static final byte ZONE_NOSUMMONFRIEND = 12;

	public static final byte ZONE_FORT = 13;

	public static final byte ZONE_NOSTORE = 14;

	public static final byte ZONE_TOWN = 15;

	public static final byte ZONE_SCRIPT = 16;

	public static final byte ZONE_NOHQ = 17;

	private final byte[] _zones = new byte[18];

	protected byte _zoneValidateCounter = 4;

	/** Table containing all skillId that are disabled */
	protected List<Integer> _disabledSkills;

	/** L2Charcater targeted by the L2Character */
	protected L2Object _target;

	// set by the start of attack, in game ticks
	private int _attackEndTime;

	/** Table of calculators containing all standard NPC calculator (ex : ACCURACY_COMBAT, EVASION_RATE */
	private static final Calculator[] NPC_STD_CALCULATOR;

	static {
		NPC_STD_CALCULATOR = Formulas.getStdNPCCalculators();
	}

	protected L2CharacterAI _ai;

	/** Future Skill Cast */
	//	protected Future<?> _skillCast;
	protected Timer _skillCast;

	/**
	 * Movement data of this L2Character
	 */
	protected MoveData _move = new MoveData();

	// =========================================================
	// NEED TO ORGANIZE AND MOVE TO PROPER PLACE
	/** This class permit to the L2Character AI to obtain informations and uses L2Character method */
	public class AIAccessor {
		public AIAccessor() {
		}

		/**
		 * Return the L2Character managed by this Accessor AI.<BR>
		 * <BR>
		 */
		public L2Character getActor() {
			return L2Character.this;
		}

		/**
		 * Accessor to L2Character moveToLocation() method with an interaction area.<BR>
		 * <BR>
		 */
		public void moveTo(int x, int y, int offset) {
			L2Character.this.moveToLocation(x, y, offset);
		}

		/**
		 * Accessor to L2Character moveToLocation() method without interaction area.<BR>
		 * <BR>
		 */
		public void moveTo(int x, int y) {
			L2Character.this.moveToLocation(x, y, 0);
		}

		/**
		 * Accessor to L2Character doAttack() method.<BR>
		 * <BR>
		 */
		public void doAttack(L2Character target) {
			L2Character.this.doAttack(target);
		}

		/**
		 * Accessor to L2Character doCast() method.<BR>
		 * <BR>
		 */
		public void doCast(L2Skill skill) {
			L2Character.this.doCast(skill);
		}

		/**
		 * Create a NotifyAITask.<BR>
		 * <BR>
		 */
		public NotifyAITask newNotifyTask(CtrlEvent evt) {
			return new NotifyAITask(evt);
		}
	}

	//	 =========================================================
	// Constructor
	/**
	 * Constructor of L2Character.<BR>
	 * <BR>
	 * 
	 * <B><U> Concept</U> :</B><BR>
	 * <BR>
	 * Each L2Character owns generic and static properties (ex : all Keltir have the same number of HP...). All of those properties are stored in a different template for each type of L2Character. Each template is loaded once in the server cache memory (reduce memory use). When a new instance of L2Character is spawned, server just create a link between the instance and the template This link is stored in <B>_template</B><BR>
	 * <BR>
	 * 
	 * <B><U> Actions</U> :</B><BR>
	 * <BR>
	 * <li>Set the _template of the L2Character</li> <li>Set _overloaded to false (the charcater can take more items)</li><BR>
	 * <BR>
	 * 
	 * <li>If L2Character is a L2NPCInstance, copy skills from template to object</li> <li>If L2Character is a L2NPCInstance, link _calculators to NPC_STD_CALCULATOR</li><BR>
	 * <BR>
	 * 
	 * <li>If L2Character is NOT a L2NPCInstance, create an empty _skills slot</li> <li>If L2Character is a L2PcInstance or L2Summon, copy basic Calculator set to object</li><BR>
	 * <BR>
	 * 
	 * @param objectId
	 *            Identifier of the object to initialized
	 * @param template
	 *            The L2CharTemplate to apply to the object
	 */
	public L2Character(long objectId, CharTemplate template) {
		super(objectId);
		initCharStat();
		initCharStatus();

		// Set its template to the new L2Character
		_template = template;

		if (template != null && this instanceof L2Npc) {
			// Copy the Standard Calcultors of the L2NPCInstance in _calculators
			_calculators = NPC_STD_CALCULATOR;

			// Copy the skills of the L2NPCInstance from its template to the L2Character Instance
			// The skills list can be affected by spell effects so it's necessary to make a copy
			// to avoid that a spell affecting a L2NPCInstance, affects others L2NPCInstance of the same type too.
			_skills = ((NpcTemplate) template).getSkills();
			if (_skills != null) {
				for (Map.Entry<Integer, L2Skill> skill : _skills.entrySet())
					addStatFuncs(skill.getValue().getStatFuncs(null, this));
			}
		} else {
			// If L2Character is a L2PcInstance or a L2Summon, create the basic calculator set
			_calculators = new Calculator[Stats.NUM_STATS];

			if (this instanceof L2Summon) {
				// Copy the skills of the L2Summon from its template to the L2Character Instance
				// The skills list can be affected by spell effects so it's necessary to make a copy
				// to avoid that a spell affecting a L2Summon, affects others L2Summon of the same type too.
				_skills = ((NpcTemplate) template).getSkills();
				if (_skills != null) {
					for (Map.Entry<Integer, L2Skill> skill : _skills.entrySet())
						addStatFuncs(skill.getValue().getStatFuncs(null, this));
				}
			} else {
				// Initialize the FastMap _skills to null
				_skills = new FastMap<Integer, L2Skill>();
			}

			Formulas.addFuncsToNewCharacter(this);
		}

		//在设置了模板、技能等信息之后初始化AI，因为AI需要模板的类型和技能数据
		initAI();

		setIsInvul(true);

		//		_status.initCharStatusUpdateValues();
	}

	/**
	 * 子类override此方法
	 */
	public void initAI() {
	}

	/**
	 * Returns character inventory, default null, overridden in L2Playable types and in L2NPcInstance
	 */
	public Inventory getInventory() {
		return null;
	}

	public boolean destroyItemByItemId(String process, int itemId, int count, L2Object reference, boolean sendMessage) {
		// Default: NPCs consume virtual items for their skills
		// TODO: should be logged if even happens.. should be false
		return true;
	}

	public boolean destroyItem(String process, long objectId, int count, L2Object reference, boolean sendMessage) {
		// Default: NPCs consume virtual items for their skills
		// TODO: should be logged if even happens.. should be false
		return true;
	}

	/**
	 * 检查是否在指定的zone内部
	 * 
	 * @param zone
	 * @return
	 */
	public final boolean isInsideZone(final byte zone) {
		switch (zone) {
		case ZONE_PVP:
			if (InstanceManager.getInstance().getInstance(getInstanceId()).isPvPInstance())
				return true;
			return _zones[ZONE_PVP] > 0 && _zones[ZONE_PEACE] == 0;
		case ZONE_PEACE:
			if (InstanceManager.getInstance().getInstance(getInstanceId()).isPvPInstance())
				return false;
		}
		return _zones[zone] > 0;
	}

	/**
	 * 
	 * @param zone
	 * @param state
	 */
	public final void setInsideZone(final byte zone, final boolean state) {
		if (state)
			_zones[zone]++;
		else {
			_zones[zone]--;
			if (_zones[zone] < 0)
				_zones[zone] = 0;
		}
	}

	/**
	 * 这将返回true，如果player被转换，但如果player没有改变，将返回false。
	 * 
	 * @return 转换的状态
	 */
	public boolean isTransformed() {
		return false;
	}

	/**
	 * This will untransform a player if they are an instance of L2Pcinstance and if they are transformed.
	 * 
	 * @return untransform
	 */
	public void untransform() {
		// Just a place holder
	}

	/**
	 * This will return true if the player is GM,<br>
	 * but if the player is not GM it will return false.
	 * 
	 * @return GM status
	 */
	public boolean isGM() {
		return false;
	}

	//	//Hp更新区间
	//	private int _hpUpdateIncCheck;
	//
	//	private int _hpUpdateDecCheck;
	//
	//	//hp更新间隔值
	//	private int _hpUpdateInterval;
	//
	//	protected final int _barPixels = 46;
	//
	//	protected void initCharStatusUpdateValues() {
	//		_hpUpdateInterval = getMaxHp() / _barPixels; // MAX_HP div MAX_HP_BAR_PX HP更新间隔
	//
	//		_hpUpdateInterval *= 3;
	//		_hpUpdateIncCheck = getMaxHp();
	//		_hpUpdateDecCheck = getMaxHp() - _hpUpdateInterval;
	//	}

	public void onDecay() {
		decayMe();
		_effects.clear();
	}

	/**
	 * 一般性角色（如：NPC，pet等）的传送处理逻辑。（玩家传送处理有点不同所以在pcinstance里面override了）
	 */
	public void onTeleported(int id, int x, int y) {
		if (!isTeleporting())
			return;
		spawnMe(id, x, y);
		setIsTeleporting(false);
		if (isPendingRevive())
			doRevive();
	}

	// =========================================================
	// Method - Public

	/**
	 * 给所有已知列表中的所有玩家发送一个服务器包<BR>
	 * <BR>
	 * 
	 */
	public void broadcastPacket(L2GameServerPacket mov) {
		FuzePcInstance pc = getActingPlayer();
		if (pc != null) {
			pc.sendPacket(mov);
		}
		Collection<FuzePcInstance> plrs = getKnownList().getKnownPlayers().values();
		for (FuzePcInstance player : plrs)
			player.sendPacket(mov);

	}

	//	/**
	//	 * 返回是否需要HP更新
	//	 * @return boolean
	//	 */
	//	protected boolean needHpUpdate(int barPixels) {
	//		int currentHp = getCurrentHp();
	//
	//		//如果 当前HP小于1， 或者 当前最大的HP没有超出 显示的像素。  那么一直更新
	//		if (currentHp <= 1 || getMaxHp() < barPixels)
	//			return true;
	//
	//		//如果当前Hp小于Hp更新的下限 或者 当前Hp大于Hp更新的上限
	//		//那么需要更新 Hp, 并且重新计算下次Hp更新的上下限
	//		if (currentHp <= _hpUpdateDecCheck || currentHp >= _hpUpdateIncCheck) {
	//
	//			if (currentHp == getMaxHp()) {
	//				//如果当前Hp=最大的Hp, 那么下次Hp的更新上限为最大Hp+1; 下限为最大Hp- Hp的更新间隔值
	//				_hpUpdateIncCheck = currentHp + 1;
	//				_hpUpdateDecCheck = currentHp - _hpUpdateInterval;
	//			} else {
	//				//如果当前Hp不等于最大Hp。  那么计算出当前Hp是 更新间隔值的 倍率
	//				//下次Hp更新下限为 更新倍率*Hp更新间隔值. Hp更新上限值为下限值+更新间隔值
	//				int intMulti = currentHp / _hpUpdateInterval;
	//
	//				_hpUpdateDecCheck = _hpUpdateInterval * intMulti;
	//				_hpUpdateIncCheck = _hpUpdateDecCheck + _hpUpdateInterval;
	//			}
	//
	//			return true;
	//		}
	//
	//		return false;
	//	}

	/**
	 * 发送一个StatusUpdate给角色，通知状态监听列表中的角色当前此角色的HP，MP
	 * 
	 */
	public void broadcastStatusUpdate() {
		if (getStatus().getStatusListener().isEmpty())
			return;

		if (_log.isDebugEnabled())
			_log.debug("Broadcast Status Update for " + getObjectId() + "(" + getName() + "). HP: " + getCurrentHp());

		// Create the Server->Client packet StatusUpdate with current HP and MP
		StatusUpdate su = new StatusUpdate(getObjectId());
		su.addAttribute(StatusUpdate.HP, getCurrentHpRadio());
		su.addAttribute(StatusUpdate.MP, getCurrentMpRadio());

		// Go through the StatusListener
		// Send the Server->Client packet StatusUpdate with current HP and MP
		for (L2Character temp : getStatus().getStatusListener()) {
			try {
				temp.sendPacket(su);
			} catch (NullPointerException e) {
			}
		}

	}

	/**
	 * 发送给客户端一个数据包，此方法由子类覆盖<BR>
	 * <BR>
	 * 
	 */
	public void sendPacket(L2GameServerPacket mov) {
		// default implementation
	}

	/**
	 * 发送给客户端一个消息，此方法由子类覆盖<BR>
	 * <BR>
	 * 
	 * <B><U> Overridden in </U> :</B><BR>
	 * <BR>
	 * <li>L2PcInstance</li><BR>
	 * <BR>
	 */
	public void sendMessage(String text) {
		// default implementation
	}

	public void teleToLocation(int id, Location loc) {
		int x = loc.getX();
		int y = loc.getY();
		teleToLocation(id, x, y);
	}

	/**
	 * 如果有必要传送L2Character和他的Pet.<BR>
	 * <BR>
	 * 
	 * <B><U> Actions</U> :</B><BR>
	 * <BR>
	 * <li>Stop the movement of the L2Character</li> <li>设置L2Object的x,y,z的坐标，并且如有必要改变它的_worldRegion</li> <li>Send a Server->Client packet TeleportToLocationt to the L2Character AND to all L2PcInstance in its _KnownPlayers</li> <li>如有必要改变Pet的位置</li><BR>
	 * <BR>
	 * 
	 */
	public void teleToLocation(int id, int x, int y) {
		setIsTeleporting(true);
		setTarget(null);

		getAI().setIntention(CtrlIntention.AI_INTENTION_ACTIVE);

		if (_log.isDebugEnabled())
			_log.debug("Teleporting to: " + x + ", " + y);

		// 删除对象从他原来的位置上
		decayMe();

		onTeleported(id, x, y);

	}

	// =========================================================
	// Method - Private
	/**
	 * 对某个目标发起物理攻击 (Simple, Bow, Pole or Dual).<BR>
	 * <BR>
	 * 
	 * <B><U> Actions</U> :</B><BR>
	 * <BR>
	 * <li>Get the active weapon (always equiped in the right hand)</li><BR>
	 * <BR>
	 * <li>If weapon is a bow, check for arrows, MP and bow re-use delay (if necessary, equip the L2PcInstance with arrows in left hand)</li> <li>If weapon is a bow, consume MP and set the new period of bow non re-use</li><BR>
	 * <BR>
	 * <li>Get the Attack Speed of the L2Character (delay (in milliseconds) before next attack)</li> <li>Select the type of attack to start (Simple, Bow, Pole or Dual) and verify if SoulShot are charged then start calculation</li> <li>If the Server->Client packet Attack contains at least 1 hit, send the Server->Client packet Attack to the L2Character AND to all L2PcInstance in the _KnownPlayers of the L2Character</li> <li>Notify AI with EVT_READY_TO_ACT</li><BR>
	 * <BR>
	 * 
	 * @param target
	 *            The L2Character targeted
	 * 
	 */
	protected void doAttack(L2Character target) {
		if (_log.isDebugEnabled())
			_log.debug(getName() + " doAttack: target=" + target);

		if (isAttackingDisabled())//如果攻击被禁止了
			return;

		if (isInsidePeaceZone(this, target)) {
			getAI().setIntention(CtrlIntention.AI_INTENTION_ACTIVE);
			return;
		}

		// 获取角色的攻击速度（两次攻击的时间间隔，到下一攻击的延迟毫秒表示）
		int timeAtk = getPAtkSpd();

		Attack attack = new Attack(target);

		boolean hitted = doAttackHitSimple(attack, target);

		// 如果双方都不在PVP状态下，进入PVP状态
		//		FuzePcInstance player = getActingPlayer();
		//
		//		if (player != null) {
		//			if (player.getPet() != target)
		//				player.updatePvPStatus(target);
		//		}

		target.sendPacket(attack);
		sendPacket(attack);

		_attackEndTime = GameTimeController.getGameTicks() + timeAtk;

		if (target instanceof FuzePcInstance) {
			((FuzePcInstance) target).updateEndurance(FuzePcInstance.REDUCEENDU_ATK, FuzePcInstance.ATKED_ENDURANCE);
		}
		
		if( this instanceof FuzePcInstance ){
			((FuzePcInstance) this).updateEndurance(FuzePcInstance.REDUCEENDU_ATKING, FuzePcInstance.ATKING_ENDURANCE);
		}

	}

	/**
	 * 发起简单的攻击.<BR>
	 * <BR>
	 * 
	 * <B><U> Actions</U> :</B><BR>
	 * <BR>
	 * <li>Calculate if hit is missed or not</li> <li>If hit isn't missed, calculate if shield defense is efficient</li> <li>If hit isn't missed, calculate if hit is critical</li> <li>If hit isn't missed, calculate physical damages</li> <li>Create a new hit task with Medium priority</li> <li>Add this hit to the Server-Client packet Attack</li><BR>
	 * <BR>
	 * 
	 * @param attack
	 *            Server->Client packet Attack in which the hit will be added
	 * @param target
	 *            The L2Character targeted
	 * 
	 * @return True if the hit isn't missed
	 * 
	 */

	private boolean doAttackHitSimple(Attack attack, L2Character target) {
		/*
		 * 如果攻击者等级>=被攻击者
		        未命中=max(0,1000-(攻命中率-防闪避率+min(100,20*(max(3,攻等级-防等级)-3))))
		如果攻击等级<被攻击者
		    未命中= max (0,1000-(攻命中率-防闪避率-min(100,20*(max(3,防等级-攻等级)-3))))
		    暴击=攻击方暴击率
		   命中=min(1000,max(0,1000-未命中-暴击))
		备注：
		这个算法是通过一次随机完成的，随机数值[0，1000)。
		 暴击的情况下，就是攻击方的修正后的攻击（包含技能）*1.5，然后再做其他运算。

		 * 
		 */

		// 计算miss量
		int miss = Formulas.calcHitMiss(this, target);

		//获得暴击量
		int crit = getCriticalHit();

		boolean miss1 = false;
		boolean crit1 = false;

		int rnd = Rnd.get(0, 1000);

		if (rnd < miss) {
			miss1 = true;
		} else if (rnd < miss + crit) {
			crit1 = true;
		}

		int damage1 = 0;
		int abourtDam = 0;

		if (!miss1) {
			damage1 = (int)Formulas.calcPhysDam(this, target, null, crit1);
			abourtDam = (int) (target.calcStat(Stats.ABSORB_DAMAGE, 0, null, null));
		}

		// Add this hit to the Server-Client packet Attack
		damage1 = damage1 - abourtDam;
		if (damage1 <= 0) {
			damage1 = 0;
		}
		if (!miss1 && abourtDam <= 0 && damage1 == 0) {
			damage1 = 1;
		}

		onHitTimer(target, damage1, crit1, miss1);
		attack.hit(damage1, miss1, crit1);

		/**
		 * 通知感兴趣的监听器 做了一次攻击
		 */
		ListenArg arg = new ListenArg();
		arg._stat = EffectCountBuff.STATS;
		arg._val = 1;
		notify(arg);
		// Return true if hit isn't missed
		return !miss1;
	}

	/**
	 * 处理物理攻击发生时的逻辑.<BR>
	 * <BR>
	 * 
	 * <B><U> Actions</U> :</B><BR>
	 * <BR>
	 * <li>If the attacker/target is dead or use fake death, notify the AI with EVT_CANCEL and send a Server->Client packet ActionFailed (if attacker is a L2PcInstance)</li> <li>If attack isn't aborted, send a message system (critical hit, missed...) to attacker/target if they are L2PcInstance</li> <li>If attack isn't aborted and hit isn't missed, reduce HP of the target and calculate reflection damage to reduce HP of attacker if necessary</li> <li>if attack isn't aborted and hit isn't missed, manage attack or cast break of the target (calculating rate, sending message...)</li><BR>
	 * <BR>
	 * 
	 * @param target
	 *            The L2Character targeted
	 * @param damage
	 *            Nb of HP to reduce
	 * @param crit
	 *            True if hit is critical
	 * @param miss
	 *            True if hit is missed
	 * @param soulshot
	 *            True if SoulShot are charged
	 * @param shld
	 *            True if shield is efficient
	 * 
	 */
	protected void onHitTimer(L2Character target, int damage, boolean crit, boolean miss) {
		// If the attacker/target is dead or use fake death, notify the AI with EVT_CANCEL
		// and send a Server->Client packet ActionFailed (if attacker is a L2PcInstance)

		if (target == null || target.isDead()) {
			getAI().notifyEvent(CtrlEvent.EVT_CANCEL);
			return;
		}

		if (miss) {
			// ON_EVADED_HIT 
			// 处理目标躲避技能时 会触发的技能
			if (target.getChanceSkills() != null)
				target.getChanceSkills().onEvadedHit(this);
		}

		if (!miss && damage > 0) {
			// 减目标的HP
			//目标吸收的伤害值
			int abourtDam = (int) (target.calcStat(Stats.ABSORB_DAMAGE, 0, null, null));
			if (damage <= abourtDam) {
				abourtDam = damage;
			}
			if (abourtDam > 0) {
				ListenArg arg = new ListenArg();
				arg._stat = Stats.ABSORB_DAMAGE.ordinal();
				arg._val = abourtDam;
				target.notify(arg);
			}
			//反射伤害
			int refectDam = (int) ((int) damage * target.calcStat(Stats.REFLECT_DAMAGE_PERCENT, 0, null, null));
			damage = damage - abourtDam;

			if (abourtDam <= 0 && damage == 0) {
				damage = 1;
			}
			//减目标的血
			if (damage > 0)
				target.reduceCurrentHp(damage, this, null);

			//反射伤害
			if (refectDam > 0 && getActingPlayer() != null) {
				getActingPlayer().reduceCurrentHp(refectDam, target, null);
			}

			// 通知目标AI受到攻击
			if (target.hasAI())
				target.getAI().notifyEvent(CtrlEvent.EVT_ATTACKED, this);

			// Maybe launch chance skills on us
			if (_chanceSkills != null)
				_chanceSkills.onHit(target, false, crit);

			// Maybe launch chance skills on target
			if (target.getChanceSkills() != null)
				target.getChanceSkills().onHit(this, true, crit);
		}

		// 发动武器的特殊效果
		L2Weapon activeWeapon = getActiveWeaponItem();

		if (activeWeapon != null)
			activeWeapon.getSkillEffects(this, target, crit);
	}

	/**
	 * 管理施法任务 (casting and interrupt time, re-use delay...) and display the casting bar and animation on client.<BR>
	 * <BR>
	 * 
	 * <B><U> Actions</U> :</B><BR>
	 * <BR>
	 * <li>Verify the possibilty of the the cast : skill is a spell, caster isn't muted...</li> <li>Get the list of all targets (ex : area effects) and define the L2Charcater targeted (its stats will be used in calculation)</li> <li>Calculate the casting time (base + modifier of MAtkSpd), interrupt time and re-use delay</li> <li>Send a Server->Client packet MagicSkillUser (to diplay casting animation), a packet SetupGauge (to display casting bar) and a system message</li> <li>Disable all skills during the casting time (create a task EnableAllSkills)</li> <li>Disable the skill during the re-use delay (create a task EnableSkill)</li> <li>Create a task MagicUseTask (that will call method onMagicUseTimer) to launch the Magic Skill at the end of the casting time</li><BR>
	 * <BR>
	 * 
	 * @param skill
	 *            The L2Skill to use TODO: TODO: 添加一个 doCast方法。 用于物品使用时 doCast(L2Skill skill, L2ItemInstance item) 同时需要修改 beginCast为beginCast(L2Skill skill, L2ItemInstance item = null ) 修改beginCast中的流程， 当item不为空时， 通知客户端物品冷却
	 */
	public void doCast(L2Skill skill) {
		beginCast(skill);
	}

	/**
	 * 开始施法
	 * 
	 * @param skill
	 */
	private void beginCast(L2Skill skill) {
		//检查技能的施法条件
		if (!checkDoCastConditions(skill)) {//如果不能释放技能
			if (this instanceof FuzePcInstance) {
				getAI().setIntention(AI_INTENTION_ACTIVE);
			}
			if (_log.isDebugEnabled()) {
				_log.debug("技能释放条件不正确");
			}
			return;
		}

		// 设置在Skill Type和Target Type函数中定义skill的目标
		L2Character target = null;
		// Get all possible targets of the skill in a table in function of the skill target type
		L2Object[] targets = skill.getTargetList(this);

		//群体魔法 
		boolean doit = false;

		switch (skill.getTargetType()) {
		case TARGET_GROUND:
			target = this;
			break;
		case TARGET_SELF:
		case TARGET_PET:
		case TARGET_SUMMON:
		case TARGET_OWNER_PET:
		case TARGET_PARTY:
			doit = true;
		default:
			if (targets.length == 0) {//如果没有目标
				// Send a Server->Client packet ActionFailed to the L2PcInstance
				if (this instanceof FuzePcInstance) {
					//					sendPacket(ActionFailed.STATIC_PACKET);
					getAI().setIntention(AI_INTENTION_ACTIVE);
				}
				if (_log.isDebugEnabled()) {
					_log.debug("角色目标链为空");
				}
				return;
			}

			switch (skill.getSkillType()) {
			case BUFF:
			case HEAL:
			case MANAHEAL:
			case REFLECT:
				doit = true;
				break;
			}// end switch (skill.getSkillType())
			if (doit) {
				//群体魔法 可能会影响很多目标。 但是施放技能时的目标放在目标链的第一个位置 
				target = (L2Character) targets[0];

				if (this instanceof FuzePcInstance && target instanceof FuzePcInstance && target.getAI().getIntention() == CtrlIntention.AI_INTENTION_ATTACK) {
					switch (skill.getSkillType()) {
					case BUFF:
					case HOT:
					case HEAL:
					case HEAL_PERCENT:
					case MANAHEAL:
					case MANAHEAL_PERCENT:
					case BALANCE_LIFE:
						target.setLastBuffer(this);
						break;
					}// end 

				}// end if (this instanceof L2PcInstance
			} else { // 获得目标， 非群体魔法
				target = (L2Character) getTarget();
			}// end if (doit)

		}// end switch (skill.getTargetType())

		if (target == null) {
			if (this instanceof FuzePcInstance) {
				//				sendPacket(ActionFailed.STATIC_PACKET);
				getAI().setIntention(AI_INTENTION_ACTIVE);
			}
			if (_log.isDebugEnabled()) {
				_log.debug("角色目标为空");
			}
			return;
		}

		// 获取这个技能的等级
		int level = skill.getLevel();

		if (level < 1)
			level = 1;

		// 获得这个技能的基本公共冷却时间
		int coolTime = skill.getCoolTime();

		//冷却时间修正
		if (coolTime > 0)
			coolTime = Formulas.calcAtkSpd(this, skill, coolTime);

		// Init the reuse time of the skill
		//技能冷卻時間, 可能會被修正
		int reuseDelay = skill.getReuseDelay();

		// 技能放入冷却管理中
		if (reuseDelay > 0) {
			addTimeStamp(skill, skill._itemid, reuseDelay);
			//把技能加入到 限制列表中，当冷却时间到时 会移除掉这个限制
			disableSkill(skill.getId(), reuseDelay);
		}

		//不在广播，  这里只单独发送给客户端
		//发送技能冷却相关的 信息给客户端
		sendPacket(new MagicSkillUse(this, target, skill, reuseDelay));
		//broadcastPacket( new MagicSkillUse( this, target, skill, reuseDelay ) );

		//		if (this instanceof FuzePcInstance) { //发送使用的技能名称
		//
		//			SystemMessage sm = new SystemMessage(SystemMessageId.USE_S1);
		//			sm.addSkillName(skill);
		//			sendPacket(sm);
		//
		//		}

		onMagicLaunchedTimer(targets, skill, reuseDelay);

	}

	/**
	 * 检查是否可以施法
	 * 
	 * @param skill
	 * @return
	 */
	private boolean checkDoCastConditions(L2Skill skill) {
		if (skill == null || isSkillDisabled(skill.getId())) {//如果技能是空或者是被禁止的技能
			// Send a Server->Client packet ActionFailed to the L2PcInstance
			//			sendPacket(ActionFailed.STATIC_PACKET);
			return false;
		}

		if (getCurrentMp() < skill.getMpConsume()) {// 检查是否有要求的足够的MP
			// Send a System Message to the caster
			sendPacket(new SystemMessage(SystemMessageId.NOT_ENOUGH_MP));

			// Send a Server->Client packet ActionFailed to the L2PcInstance
			//			sendPacket(ActionFailed.STATIC_PACKET);
			return false;
		}

		if (getCurrentHp() <= skill.getHpConsume()) {//检查是否有要求的足够的HP
			// Send a System Message to the caster
			sendPacket(new SystemMessage(SystemMessageId.NOT_ENOUGH_HP));
			// Send a Server->Client packet ActionFailed to the L2PcInstance
			//			sendPacket(ActionFailed.STATIC_PACKET);
			return false;
		}

		switch (skill.getSkillType()) {
		case SUMMON_TRAP: {
			if (isInsideZone(ZONE_PEACE)) {//如果是在安全区不能投放恶意技能
				if (this instanceof FuzePcInstance)
					((FuzePcInstance) this).sendPacket(new SystemMessage(SystemMessageId.A_MALICIOUS_SKILL_CANNOT_BE_USED_IN_PEACE_ZONE));
				return false;
			}
			if (this instanceof FuzePcInstance && ((FuzePcInstance) this).getTrap() != null) {//如果已经投放了一个陷阱
				return false;
			}
			break;
		}
		case SUMMON: {
			if (this instanceof FuzePcInstance && (((FuzePcInstance) this).getPet() != null || ((FuzePcInstance) this).isMounted())) {//不能重复招宠物
				if (_log.isDebugEnabled())
					_log.debug("player has a pet already. ignore summon skill");
				sendPacket(new SystemMessage(SystemMessageId.YOU_ALREADY_HAVE_A_PET));
				return false;
			}
		}
		}// end switch (skill.getSkillType())

		if (!skill.isPotion()) {//如果不是药水属性技能
			// 如果不是药水 那么需要检查 角色是否被沉默
			if (skill.isMagic()) {//如果是一个魔法系技能
				if (isMuted()) {//如果魔法禁言
					return false;
				}
			} else {//如果是一个物理系技能 
				//检查是否处于物理攻击无效或不能物理攻击状态
				if (isPhysicalMuted() || isPhysicalAttackMuted()) {
					return false;
				}
			}
		}// end if (!skill.isPotion())

		// 检查是否释放者拥有需要的装备
		if (!skill.getWeaponDependancy(this)) {
			return false;
		}

		//释放技能道具 是否满足
		int[] itemConsume = skill.getItemConsumeId();
		if (itemConsume != null && itemConsume.length > 0 && getInventory() != null) {
			for (int i = 0; i < itemConsume.length; i++) {
				L2ItemInstance requiredItems = getInventory().getItemByItemId(itemConsume[i]);
				if (requiredItems == null || requiredItems.getCount() < skill.getItemConsume()[i]) {
					// Checked: when a summon skill failed, server show required consume item count
					// 召唤类技能 通知客户端 需要的道具数量
					if (skill.getSkillType() == L2SkillType.SUMMON) {
						SystemMessage sm = new SystemMessage(SystemMessageId.SUMMONING_SERVITOR_COSTS_S2_S1);
						sm.addItemName(itemConsume[i]);
						sm.addNumber(skill.getItemConsume()[i]);
						sendPacket(sm);
						return false;
					} else {
						// Send a System Message to the caster
						sendPacket(new SystemMessage(SystemMessageId.THERE_ARE_NOT_ENOUGH_NECESSARY_ITEMS_TO_USE_THE_SKILL));
						return false;
					}
				}

			}// end for( int i=0; i<itemConsume.length; i++ )

		}// end if( itemConsume.length > 0 && getInventory() != null )

		return true;

	}// end checkDoCastConditions

	/**
	 * 通知AI施法结束
	 */
	public final void abortCast() {
		//通知AI施法结束
		if (this instanceof FuzePcInstance)
			getAI().notifyEvent(CtrlEvent.EVT_FINISH_CASTING); // setting back previous intention
	}

	/**
	 * Manage the magic skill launching task (MP, HP, Item consummation...) and display the magic skill animation on client.<BR>
	 * <BR>
	 * 
	 * <B><U> Actions</U> :</B><BR>
	 * <BR>
	 * <li>Send a Server->Client packet MagicSkillLaunched (to display magic skill animation) to all L2PcInstance of L2Charcater _knownPlayers</li> <li>Consumme MP, HP and Item if necessary</li> <li>Send a Server->Client packet StatusUpdate with MP modification to the L2PcInstance</li> <li>Launch the magic skill in order to calculate its effects</li> <li>If the skill type is PDAM, notify the AI of the target with AI_INTENTION_ATTACK</li> <li>Notify the AI of the L2Character with EVT_FINISH_CASTING</li><BR>
	 * <BR>
	 * 
	 * <FONT COLOR=#FF0000><B> <U>Caution</U> : A magic skill casting MUST BE in progress</B></FONT><BR>
	 * <BR>
	 * 
	 * @param skill
	 *            The L2Skill to use
	 * 
	 */
	public void onMagicLaunchedTimer(L2Object[] targets, L2Skill skill, int reuseDelay) {
		if (skill == null || targets == null || targets.length <= 0) {
			abortCast();
			if (_log.isDebugEnabled()) {
				_log.debug("角色目标为空  或 技能为空");
			}
			return;
		}

		//技能范围.如果指定了技能效果的范围， 那么将使用技能的效果范围，否则使用技能半径做为技能的效果范围
		//指定了技能效果范围的 理论上才是AOE法术， 在计算伤害的时候 需要修正目标
		int escapeRange = 0;
		if (skill.getEffectRange() > escapeRange)//技能产生的效果范围
			escapeRange = skill.getEffectRange();
		//		else if (skill.getSkillRadius() > 0 )//技能的施法范围
		//			escapeRange = skill.getSkillRadius();

		//如果是AOE法术
		//需要检查目标 是否还在效果范围内
		if (escapeRange > 0) {//如果范围不是0
			List<L2Character> targetList = new FastList<L2Character>();
			for (L2Object target : targets) {
				if (target instanceof L2Character) {
					if (!Util.checkIfInRange(escapeRange, this, target))
						continue;
					if (skill.isOffensive() && !skill.isNeutral()) {//如果是进攻型非中性的
						if (this instanceof FuzePcInstance) {
							if (((L2Character) target).isInsidePeaceZone((FuzePcInstance) this))
								continue;
						} else {
							if (((L2Character) target).isInsidePeaceZone(this, target))
								continue;
						}
					}
					targetList.add((L2Character) target);
				}
			}
			if (targetList.isEmpty()) {
				abortCast();
				if (_log.isDebugEnabled()) {
					_log.debug("释放技能是 目标链空了");
				}
				return;
			} else
				targets = targetList.toArray(new L2Character[targetList.size()]);
		}

		//如果玩家死亡
		if (isDead()) {
			getAI().notifyEvent(CtrlEvent.EVT_CANCEL);
			if (_log.isDebugEnabled()) {
				_log.debug("角色死亡");
			}
			return;
		}

		// 发送 技能启用 MagicSkillLaunched
		broadcastPacket(new MagicSkillLaunched(this, skill, getTarget()));
		if (_log.isDebugEnabled()) {
			_log.debug("发送技能播放动画");
		}

		onMagicHitTimer(targets, skill, reuseDelay);

	}

	/*
	 * Runs in the end of skill casting 
	 */
	public void onMagicHitTimer(L2Object[] targets, L2Skill skill, int coolTime) {

		if (skill == null || targets == null || targets.length <= 0) {
			abortCast();
			if (_log.isDebugEnabled()) {
				_log.debug("技能为空 或 目标连为空");
			}
			return;
		}

		try {
			// Go through targets table
			if (skill.getSkillType() == L2SkillType.BUFF) {
				for (L2Object tgt : targets) {
					if (tgt instanceof L2Playable) {
						L2Character target = (L2Character) tgt;

						if (skill._itemid == -1) {
							SystemMessage smsg = new SystemMessage(SystemMessageId.YOU_FEEL_S1_EFFECT);
							smsg.addSkillName(skill);
							target.sendPacket(smsg);
						}

						if (this instanceof FuzePcInstance && target instanceof L2Summon) {
							((L2Summon) target).updateAndBroadcastStatus(1);
						}
					}
				}
			}

			// 角色需要消耗的Mp
			int consume = skill.getMpConsume();

			if (consume > 0) {
				getStatus().reduceMp(consume);
			}

			// 角色需要消耗的HP
			consume = skill.getHpConsume();
			if (consume > 0) {

				if (consume + 1 >= getCurrentHp())
					consume = getCurrentHp() - 1;

				getStatus().reduceHp(consume, this, true);
			}

			//消耗物品
			int[] itemIds = skill.getItemConsumeId();
			if (itemIds != null && itemIds.length > 0) {
				for (int i = 0; i < itemIds.length; i++) {
					if (!destroyItemByItemId("Consume", itemIds[i], skill.getItemConsume()[i], null, false)) {
						sendPacket(new SystemMessage(SystemMessageId.NOT_ENOUGH_ITEMS));
						return;
					}
				}

			}// end if( itemIds.length > 0 )

			callSkill(skill, targets);

		} catch (NullPointerException e) {
		}

		onMagicFinalizer(skill, targets[0]);

	}

	/** Task lauching the magic skill phases */
	class MagicUseTask implements Runnable {
		L2Object[] _targets;

		L2Skill _skill;

		int _coolTime;

		int _phase;

		public MagicUseTask(L2Object[] targets, L2Skill skill, int coolTime, int phase) {
			_targets = targets;
			_skill = skill;
			_coolTime = coolTime;
			_phase = phase;
		}

		public void run() {
			try {
				switch (_phase) {
				case 1:
					onMagicLaunchedTimer(_targets, _skill, _coolTime);
					break;
				case 2:
					onMagicHitTimer(_targets, _skill, _coolTime);
					break;
				case 3:
					onMagicFinalizer(_skill, _targets[0]);
					break;
				default:
					break;
				}
			} catch (Exception e) {
				_log.error("Failed executing MagicUseTask.", getObjectId(), e);
			}
		}
	}// end class MagicUseTask implements Runnable 

	/**
	 * 启动魔法技能，并计算在影响在目标列表中各个目标的效果。
	 * 
	 * @param skill
	 *            The L2Skill to use
	 * @param targets
	 *            The table of L2Object targets
	 * 
	 */
	public void callSkill(L2Skill skill, L2Object[] targets) {
		try {

			//Get the skill handler corresponding to the skill type (PDAM, MDAM, SWEEP...) started in gameserver
			ISkillHandler handler = SkillHandler.getInstance().getSkillHandler(skill.getSkillType());

			// Initial checks
			for (L2Object trg : targets) {
				if (trg instanceof L2Character) {
					// Set some values inside target's instance for later use
					L2Character target = (L2Character) trg;

					// _chanceSkills 在添加技能的时候 自动添加进去

					//如果角色身上存在触发型技能
					//可能发生在角色自身上的概率性技能
					if (_chanceSkills != null)
						_chanceSkills.onSkillHit(target, false, skill.isMagic(), skill.isOffensive());
					//可能发生在目标身上的概率性技能
					if (target.getChanceSkills() != null)
						target.getChanceSkills().onSkillHit(this, true, skill.isMagic(), skill.isOffensive());

				}

			}// end for (L2Object trg : targets)

			//发动魔法技能和计算它产生的效果
			if (handler != null) {
				handler.useSkill(this, skill, targets);
			} else
				skill.useSkill(this, targets);

			int addSkillId = (int) calcStat(Stats.ADD_SKILL, 0, null, skill);
			L2Skill addSk = SkillTable.getInstance().getHashSkill(addSkillId);
			if (addSk != null) {
				//释放新增的技能效果
				for (L2Object trg : targets) {
					if (trg instanceof L2Character) {

						addSk.getEffects(this, (L2Character) trg);
					}

				}// end for

			}

			FuzePcInstance player = getActingPlayer();

			if (player != null) {

				for (L2Object target : targets) {
					// EVT_ATTACKED and PvPStatus
					if (target instanceof L2Character) {
						if (skill.isNeutral()) {//如果是中性的技能
							// no flags
						} else if (skill.isOffensive()) {//如果是进攻型技能
							if (target instanceof FuzePcInstance || target instanceof L2Summon || target instanceof L2Trap) {
								//如果不是仇恨类的技能
								if (skill.getSkillType() != L2SkillType.AGGREDUCE && skill.getSkillType() != L2SkillType.AGGREDUCE_CHAR && skill.getSkillType() != L2SkillType.AGGREMOVE) {
									//通知目标的AI被攻击了
									((L2Character) target).getAI().notifyEvent(CtrlEvent.EVT_ATTACKED, player);
								}

								//更新PVP状态
								if (!(target instanceof L2Summon) || player.getPet() != target)
									player.updatePvPStatus((L2Character) target);

							} else if (target instanceof L2Attackable) {//如果是一个攻击性NPC
								switch (skill.getSkillType()) {
								case AGGREDUCE:
								case AGGREDUCE_CHAR:
								case AGGREMOVE:
									break;
								default:

									//通知目标的AI被攻击了
									((L2Character) target).getAI().notifyEvent(CtrlEvent.EVT_ATTACKED, this);
									break;
								}
							}
						} // else if (skill.isOffensive())
						else {
							if (target instanceof FuzePcInstance) {
								// Casting non offensive skill on player with pvp flag set or with karma
								if (!(target.equals(this) || ((FuzePcInstance) target).getPvpFlag() > 0 || ((FuzePcInstance) target).getKarma() > 0))
									player.updatePvPStatus();
							} else if (target instanceof L2Attackable && !(skill.getSkillType() == L2SkillType.SUMMON) && (!(target instanceof L2Summon) || player.getPet() != target))
								player.updatePvPStatus();
						}
					}// end if (target instanceof L2Character)
				}

			
			}

		} catch (Exception e) {
			_log.error("callSkill error", getObjectId(), e);
		}

	}// end callSkill

	/*
	 * magic最后的处理过程，在技能的hitTime+coolTime之后调用
	 */
	public void onMagicFinalizer(L2Skill skill, L2Object target) {

		// If the skill type is listed here, notify the AI of the target with AI_INTENTION_ATTACK for offensive skills the nextintention is always null unless player wants action after skill
		// Note: this might also work
		if (getAI().getNextIntention() == null) {//如果AI下一步的指令为空
			switch (skill.getSkillType()) {
			case PDAM:
				//通知AI开始攻击
				if (getTarget() instanceof L2Character && getTarget() != this && target == getTarget())
					getAI().setIntention(CtrlIntention.AI_INTENTION_ATTACK, getTarget());
				break;
			}
		}

		// Notify the AI of the L2Character with EVT_FINISH_CASTING
		getAI().notifyEvent(CtrlEvent.EVT_FINISH_CASTING);

		notifyQuestEventSkillFinished(skill, target);

		//清空玩家的施法状态
		if (this instanceof FuzePcInstance) {
			FuzePcInstance currPlayer = (FuzePcInstance) this;

			//当前技能释放结束
			//清空当前施法状态
			currPlayer.setCurrentSkill(null, false, false);
			currPlayer.notifyQuestEventUseSkill(skill);// 通知包含使用技能的任务逻辑

		}

	}// end onMagicFinalizer

	/** Task launching the function useMagic() */
	class QueuedMagicUseTask implements Runnable {
		FuzePcInstance _currPlayer;

		L2Skill _queuedSkill;

		boolean _isCtrlPressed;

		boolean _isShiftPressed;

		public QueuedMagicUseTask(FuzePcInstance currPlayer, L2Skill queuedSkill, boolean isCtrlPressed, boolean isShiftPressed) {
			_currPlayer = currPlayer;
			_queuedSkill = queuedSkill;
			_isCtrlPressed = isCtrlPressed;
			_isShiftPressed = isShiftPressed;
		}

		public void run() {
			try {
				_currPlayer.useMagic(_queuedSkill, _isCtrlPressed, _isShiftPressed);
			} catch (Exception e) {
				_log.error("Failed executing QueuedMagicUseTask.", getObjectId(), e);
			}
		}
	}

	//	 Quest event ON_SPELL_FNISHED
	private void notifyQuestEventSkillFinished(L2Skill skill, L2Object target) {
		if (this instanceof L2Npc) {
			try {
				if (((NpcTemplate) getTemplate()).getEventQuests(Quest.QuestEventType.ON_SPELL_FINISHED) != null) {
					FuzePcInstance player = target.getActingPlayer();
					for (Quest quest : ((NpcTemplate) getTemplate()).getEventQuests(Quest.QuestEventType.ON_SPELL_FINISHED)) {
						quest.notifySpellFinished(((L2Npc) this), player, skill);
					}
				}
			} catch (Exception e) {
				_log.error("Notify quest event skill finished", getObjectId(), e);
			}
		}
	}

	/**
	 * 增加技能 到 冷却管理器中
	 * 
	 * @param sk
	 *            : 技能
	 * @param itemId
	 *            : 技能相关的物品Id
	 * @param reuse
	 *            : 冷却时间
	 */
	public void addTimeStamp(L2Skill sk, int itemId, int reuse) {

	}

	/**
	 * 增加技能 到 冷却管理器中
	 * 
	 * @param sk
	 *            : 技能
	 * @param itemId
	 *            : 技能相关的物品Id
	 */
	public void addTimeStamp(L2Skill sk, int itemId) {

	}

	/**
	 * Index according to skill id the current timestamp of use.<br>
	 * <br>
	 * 
	 * @param skill
	 *            id <BR>
	 *            <B>Overridden in :</B> (L2PcInstance)
	 */
	public void removeTimeStamp(int s) {
		/***/
	}

	/**
	 * 杀掉这个L2Character.<BR>
	 * <BR>
	 * 
	 * <B><U> Actions</U> :</B><BR>
	 * <BR>
	 * <li>Set target to null and cancel Attack or Cast</li> <li>Stop movement</li> <li>Stop HP/MP/CP Regeneration task</li> <li>Stop all active skills effects in progress on the L2Character</li> <li>Send the Server->Client packet StatusUpdate with current HP and MP to all other L2PcInstance to inform</li> <li>Notify L2Character AI</li><BR>
	 * <BR>
	 * 
	 * <B><U> Overridden in </U> :</B><BR>
	 * <BR>
	 * <li>L2NpcInstance : Create a DecayTask to remove the corpse of the L2NpcInstance after 7 seconds</li> <li>L2Attackable : Distribute rewards (EXP, SP, Drops...) and notify Quest Engine</li> <li>L2PcInstance : Apply Death Penalty, Manage gain/loss Karma and Item Drop</li><BR>
	 * <BR>
	 * 
	 * @param killer
	 *            The L2Character who killed it
	 * 
	 */
	public boolean doDie(L2Character killer) {
		if (_log.isDebugEnabled())
			_log.debug("char " + this + " is dead.");
		if (isDead())// 死亡只能发送一次
			return false;

		//设置HP为0
		setCurrentHp(0);
		//设置死亡状态
		setIsDead(true);
		// 设置目标位空
		setTarget(null);
		//停止自恢复逻辑
		getStatus().stopRegeneration();

		calculateRewards(killer);//L2attackable实现的方法，分发经验给L2pcinstance

		stopAllEffectsExceptThoseThatLastThroughDeath();

		//发送此角色的HP，MP给场景上的其他玩家
		//		broadcastStatusUpdate();

		// 通知AI死亡事件
		getAI().notifyEvent(CtrlEvent.EVT_DEAD);

		//通知场景上的zone死亡角色
		if (getScene() != null)
			getScene().onDeath(this);

		//关闭角色的AI
		disableCoreAI(true);

		return true;
	}

	/**
	 * 子类完成
	 * 
	 * @param killer
	 */
	protected void calculateRewards(L2Character killer) {
	}

	/**
	 * 复活此角色，恢复HP，MP
	 * 
	 */
	public void doRevive() {
		if (!isDead())
			return;
		if (!isTeleporting()) {
			setIsPendingRevive(false);
			setIsDead(false);

			//这里只改变hp,mp的值，不发送给客户端，原来的代码这里发送给客户端但是因为监听列表内没有周围玩家，所以会导致周围的玩家看不到hp,mp的变化
			//按照原来的逻辑不需要发给客户端，只要通知玩家本身，而其他的玩家是通过单击角色来获取hp,mp信息的
			if (isReviveFullResume()) {
				_status.setCurrentHp(getMaxHp(), false);
				_status.setCurrentMp(getMaxMp(), false);
			} else {
				_status.setCurrentHp((int) (getMaxHp() * Config.RESPAWN_RESTORE_HP), false);
				_status.setCurrentMp((int) (getMaxMp() * Config.RESPAWN_RESTORE_MP), false);
			}
			//重新初始化HPMP更新阈值
			reInitCharStatusUpdate();
			//开始自恢复
			getStatus().startRegeneration();

			//在这里广播给客户端hp,mp的变化
			StatusUpdate su = new StatusUpdate(getObjectId());
			su.addAttribute(StatusUpdate.HP, getCurrentHpRadio());
			su.addAttribute(StatusUpdate.MP, getCurrentMpRadio());
			broadcastPacket(su);

			// Start broadcast status
			broadcastPacket(new Revive(this));
			if (getScene() != null)
				getScene().onRevive(this);

			//开启角色的AI
			disableCoreAI(false);
		}
	}

	// =========================================================
	// Property - Public
	/**
	 * 返回角色的AI状态机，如果AI是空则创建一个
	 */
	public L2CharacterAI getAI() {
		return _ai;
	}

	public void setAI(L2CharacterAI newAI) {
		_ai = newAI;
	}

	/** Return True if the L2Character has a L2CharacterAI. */
	public boolean hasAI() {
		return _ai != null;
	}

	public final L2Skill getLastSkillCast() {
		return _lastSkillCast;
	}

	public void setLastSkillCast(L2Skill skill) {
		_lastSkillCast = skill;
	}

	/**
	 * 是否恐惧状态
	 * 
	 * @return
	 */
	public final boolean isAfraid() {
		return (_flag & FLAG_AFRAID) != 0;
	}

	/**
	 * 设置恐惧状态
	 * 
	 * @param value
	 */
	public final void setIsAfraid(boolean value) {
		if (value)
			_flag |= FLAG_AFRAID;
		else
			_flag &= ~FLAG_AFRAID;
	}

	/** 如果角色的所有技能都不能使用时，返回true */
	public final boolean isAllSkillsDisabled() {
		return (_flag & (FLAG_ALLSKILLSDISABLED | FLAG_IMMOBILEUNTILATTACKED | FLAG_STUNNED | FLAG_SLEEPING | FLAG_PARALYZED)) != 0;
	}

	/** 如果角色不能攻击时，返回true */
	public boolean isAttackingDisabled() {
		return _attackEndTime > GameTimeController.getGameTicks() || (_flag&(FLAG_STUNNED|FLAG_IMMOBILEUNTILATTACKED|FLAG_SLEEPING|FLAG_DEAD|FLAG_PARALYZED|FLAG_PHYSICALATTACKMUTED|FLAG_AIDISABLED))!=0;
	}

	public final Calculator[] getCalculators() {
		return _calculators;
	}

	/**
	 * 是否混乱状态
	 * 
	 * @return
	 */
	public final boolean isConfused() {
		return (_flag & FLAG_CONFUSED) != 0;
	}

	/**
	 * 设置混乱状态
	 * 
	 * @param value
	 */
	public final void setIsConfused(boolean value) {
		if (value)
			_flag |= FLAG_CONFUSED;
		else
			_flag &= ~FLAG_CONFUSED;
	}

	/**
	 * 是否魔法禁言状态
	 * 
	 * @return
	 */
	public final boolean isMuted() {
		return (_flag & FLAG_MUTED) != 0;
	}

	/**
	 * 设置魔法禁言状态
	 * 
	 * @param value
	 */
	public final void setIsMuted(boolean value) {
		if (value)
			_flag |= FLAG_MUTED;
		else
			_flag &= ~FLAG_MUTED;
	}

	/**
	 * 是否物理禁言状态
	 * 
	 * @return
	 */
	public final boolean isPhysicalMuted() {
		return (_flag & FLAG_PHYSICALMUTED) != 0;
	}

	/**
	 * 设置物理禁言状态
	 * 
	 * @param value
	 */
	public final void setIsPhysicalMuted(boolean value) {
		if (value)
			_flag |= FLAG_PHYSICALMUTED;
		else
			_flag &= ~FLAG_PHYSICALMUTED;
	}

	/**
	 * 物理攻击禁言状态
	 * 
	 * @return
	 */
	public final boolean isPhysicalAttackMuted() {
		return (_flag & FLAG_PHYSICALATTACKMUTED) != 0;
	}

	/**
	 * 设置物理攻击禁言状态
	 * 
	 * @param value
	 */
	public final void setIsPhysicalAttackMuted(boolean value) {
		if (value)
			_flag |= FLAG_PHYSICALATTACKMUTED;
		else
			_flag &= ~FLAG_PHYSICALATTACKMUTED;
	}

	/**
	 * 是否死亡状态
	 * 
	 * @return
	 */
	public final boolean isDead() {
		return (_flag & FLAG_DEAD) != 0;
	}

	/**
	 * 设置死亡状态
	 * 
	 * @param value
	 */
	public final void setIsDead(boolean value) {
		if (value)
			_flag |= FLAG_DEAD;
		else
			_flag &= ~FLAG_DEAD;
	}

	/**
	 * 是否定身状态
	 * 
	 * @return
	 */
	public boolean isImmobilized() {
		return (_flag & FLAG_IMMOBILIZED) != 0;
	}

	/**
	 * 设置定身状态(只是不能行走无其他逻辑)
	 * 
	 * @param value
	 */
	public void setIsImmobilized(boolean value) {
		if (value)
			_flag |= FLAG_IMMOBILIZED;
		else
			_flag &= ~FLAG_IMMOBILIZED;
	}

	/**
	 * 是否处于静行术状态.<BR>
	 * <BR>
	 */
	public boolean isSilentMoving() {
		return (_flag & FLAG_SILENTMOVING) != 0;
	}

	/**
	 * Set the Silent Moving mode Flag.<BR>
	 * <BR>
	 */
	public void setSilentMoving(boolean value) {
		if (value)
			_flag |= FLAG_SILENTMOVING;
		else
			_flag &= ~FLAG_SILENTMOVING;
	}

	/**
	 * 是否角色已经超重
	 * 
	 * @return
	 */
	public final boolean isOverloaded() {
		return (_flag & FLAG_OVERLOADED) != 0;
	}

	/**
	 * 设置超重状态，角色不能再捡起任何的道具
	 * 
	 * @param value
	 */
	public final void setIsOverloaded(boolean value) {
		if (value)
			_flag |= FLAG_OVERLOADED;
		else
			_flag &= ~FLAG_OVERLOADED;
	}

	/**
	 * 是否瘫痪状态
	 * 
	 * @return
	 */
	public final boolean isParalyzed() {
		return (_flag & FLAG_PARALYZED) != 0;
	}

	/**
	 * 设置角色瘫痪状态
	 * 
	 * @param value
	 */
	public final void setIsParalyzed(boolean value) {
		if (value)
			_flag |= FLAG_PARALYZED;
		else
			_flag &= ~FLAG_PARALYZED;
	}

	/**
	 * 是否处于解除武装状态
	 * 
	 * @return
	 */
	public final boolean isDisarmed() {
		return (_flag & FLAG_DISARMED) != 0;
	}

	/**
	 * 设置解除武装状态
	 * 
	 * @param value
	 */
	public final void setIsDisarmed(boolean value) {
		if (value)
			_flag |= FLAG_DISARMED;
		else
			_flag &= ~FLAG_DISARMED;
	}

	/**
	 * 是否等待复活状态
	 * 
	 * @return
	 */
	public final boolean isPendingRevive() {
		return isDead() && (_flag & FLAG_PENDINGREVIVE) != 0;
	}

	/**
	 * 设置等待复活状态
	 * 
	 * @param value
	 */
	public final void setIsPendingRevive(boolean value) {
		if (value)
			_flag |= FLAG_PENDINGREVIVE;
		else
			_flag &= ~FLAG_PENDINGREVIVE;
	}

	/**
	 * 是否复活满状态
	 * 
	 * @return
	 */
	public final boolean isReviveFullResume() {
		return (_flag & FLAG_REVIVEFULLRESUME) != 0;
	}

	/**
	 * 设置复活满状态
	 * 
	 * @param value
	 */
	public final void setIsReviveFullResume(boolean value) {
		if (value)
			_flag |= FLAG_REVIVEFULLRESUME;
		else
			_flag &= ~FLAG_REVIVEFULLRESUME;
	}

	/**
	 * 是否盘绕状态
	 * 
	 * @return
	 */
	public final boolean isRooted() {
		return (_flag & FLAG_ROOTED) != 0;
	}

	/**
	 * 设置盘绕状态
	 * 
	 * @param value
	 */
	public final void setIsRooted(boolean value) {
		if (value)
			_flag |= FLAG_ROOTED;
		else
			_flag &= ~FLAG_ROOTED;
	}

	/**
	 * 是否处于直到被攻击才解除的定身状态
	 * 
	 * @return
	 */
	public final boolean isImmobileUntilAttacked() {
		return (_flag & FLAG_IMMOBILEUNTILATTACKED) != 0;
	}

	/**
	 * 设置直到被攻击才解除的定身状态
	 * 
	 * @param value
	 */
	public final void setIsImmobileUntilAttacked(boolean value) {
		if (value)
			_flag |= FLAG_IMMOBILEUNTILATTACKED;
		else
			_flag &= ~FLAG_IMMOBILEUNTILATTACKED;
	}

	/**
	 * 是否睡眠状态
	 * 
	 * @return
	 */
	public final boolean isSleeping() {
		return (_flag & FLAG_SLEEPING) != 0;
	}

	/**
	 * 设置睡眠状态
	 * 
	 * @param value
	 */
	public final void setIsSleeping(boolean value) {
		if (value)
			_flag |= FLAG_SLEEPING;
		else
			_flag &= ~FLAG_SLEEPING;
	}

	/**
	 * 是否处于震惊状态
	 * 
	 * @return
	 */
	public final boolean isStunned() {
		return (_flag & FLAG_STUNNED) != 0;
	}

	/**
	 * 设置震惊状态
	 * 
	 * @param value
	 */
	public final void setIsStunned(boolean value) {
		if (value)
			_flag |= FLAG_STUNNED;
		else
			_flag &= ~FLAG_STUNNED;
	}

	/**
	 * 是否处于背叛状态
	 * 
	 * @return
	 */
	public final boolean isBetrayed() {
		return (_flag & FLAG_BETRAYED) != 0;
	}

	/**
	 * 设置背叛状态
	 * 
	 * @param value
	 */
	public final void setIsBetrayed(boolean value) {
		if (value)
			_flag |= FLAG_BETRAYED;
		else
			_flag &= ~FLAG_BETRAYED;
	}

	/**
	 * 是否正在传送状态
	 * 
	 * @return
	 */
	public final boolean isTeleporting() {
		return (_flag & FLAG_TELEPORING) != 0;
	}

	/**
	 * 设置正在传送状态
	 * 
	 * @param value
	 */
	public void setIsTeleporting(boolean value) {
		if (value)
			_flag |= FLAG_TELEPORING;
		else
			_flag &= ~FLAG_TELEPORING;
	}

	/**
	 * 是否处于无敌状态
	 * 
	 * @return
	 */
	public boolean isInvul() {
		return (_flag & FLAG_INVUL) != 0;
	}

	/**
	 * 设置无敌状态
	 * 
	 * @param b
	 */
	public void setIsInvul(boolean value) {
		if (value)
			_flag |= FLAG_INVUL;
		else
			_flag &= ~FLAG_INVUL;
	}

	/**
	 * 通过effect设置无敌状态
	 * 
	 * @param b
	 * @param effect
	 */
	public void setIsInvulByEffect(boolean b, L2Effect effect) {
		setIsInvul(b);
		_invulEffect = effect;
	}

	public boolean isDisableAllSkills() {
		return (_flag & FLAG_ALLSKILLSDISABLED) != 0;
	}

	/**
	 * Disable all skills (set _allSkillsDisabled to True).<BR>
	 * <BR>
	 */
	public void disableAllSkills() {
		if (_log.isDebugEnabled())
			_log.debug("all skills disabled");
		_flag |= FLAG_ALLSKILLSDISABLED;
	}

	/**
	 * Enable all skills (set _allSkillsDisabled to False).<BR>
	 * <BR>
	 */
	public void enableAllSkills() {
		if (_log.isDebugEnabled())
			_log.debug("all skills enabled");
		_flag &= ~FLAG_ALLSKILLSDISABLED;
	}
	
	/**
	 * 设置是否启用AI
	 * 
	 * @param val
	 */
	public void disableCoreAI(boolean val) {
		if (val)
			_flag |= FLAG_AIDISABLED;
		else
			_flag &= ~FLAG_AIDISABLED;
	}

	/**
	 * AI是否被禁止了
	 * 
	 * @return
	 */
	public boolean isCoreAIDisabled() {
		return (_flag & FLAG_AIDISABLED) != 0;
	}

	/**
	 * 是否角色不能移动
	 * 
	 * @return
	 */
	public boolean isMovementDisabled() {
		// check for isTeleporting to prevent teleport cheating (if appear packet not received)
		return (_flag & (FLAG_STUNNED|FLAG_ROOTED|FLAG_SLEEPING|FLAG_OVERLOADED|FLAG_PARALYZED|FLAG_IMMOBILIZED|FLAG_DEAD)) != 0;
	}

	/**
	 * 是否角色不能被玩家控制
	 * 
	 * @return
	 */
	public final boolean isOutOfControl() {
		return (_flag & (FLAG_CONFUSED|FLAG_AFRAID))!=0;
	}

	/**
	 * Return the L2Summon of the L2Character.<BR>
	 * <BR>
	 * <B><U> Overridden in </U> :</B><BR>
	 * <BR>
	 * <li>L2PcInstance</li><BR>
	 * <BR>
	 */
	public L2Summon getPet() {
		return null;
	}

	/**
	 * 角色是否在奔跑状态
	 * 
	 * @return
	 */
	//	public boolean isRunning() {
	//		return _isRunning;
	//	}

	/**
	 * 设置角色奔跑状态
	 * 
	 * @param value
	 */
	//	public final void setIsRunning(boolean value) {
	//		_isRunning = value;
	//		if (getRunSpeed() != 0)
	//			broadcastPacket(new ChangeMoveType(this));
	//		if (this instanceof L2PcInstance)
	//			((L2PcInstance) this).broadcastUserInfo();
	//		else if (this instanceof L2Summon) {
	//			((L2Summon) this).broadcastStatusUpdate();
	//		} else if (this instanceof L2Npc) {
	//			Collection<L2PcInstance> plrs = getKnownList().getKnownPlayers().values();
	//
	//			for (L2PcInstance player : plrs) {
	//				if (getRunSpeed() == 0)
	//					player.sendPacket(new ServerObjectInfo((L2Npc) this, player));
	//				else
	//					player.sendPacket(new AbstractNpcInfo.NpcInfo((L2Npc) this, player));
	//			}
	//		}
	//	}
	/**
	 * 设置角色奔跑状态，并广播此人的knownlist内的所有玩家
	 */
	//	public final void setRunning() {
	//		if (!isRunning())
	//			setIsRunning(true);
	//	}
	/**
	 * 设置行走状态，并广播此人的knownlist内的所有玩家
	 */
	//	public final void setWalking() {
	//		if (isRunning())
	//			setIsRunning(false);
	//	}

	/**
	 * 获得设置成无敌状态的效果对象
	 * 
	 * @return
	 */
	public L2Effect getInvulEffect() {
		return _invulEffect;
	}

	/**
	 * 是否是不死状态
	 * 
	 * @return
	 */
	//	public boolean isUndead() {
	//		return _template.isUndead;
	//	}

	@Override
	public CharKnownList getKnownList() {
		return (CharKnownList) _knownList;
	}

	@Override
	public void initKnownList() {
		setKnownList(new CharKnownList(this));
	}

	public CharStat getStat() {
		return _stat;
	}

	/**
	 * Initializes the CharStat class of the L2Object, is overwritten in classes that require a different CharStat Type.
	 * 
	 * Removes the need for instanceof checks.
	 */
	public void initCharStat() {
		_stat = new CharStat(this);
	}

	public final void setStat(CharStat value) {
		_stat = value;
	}

	public CharStatus getStatus() {
		return _status;
	}

	/**
	 * Initializes the CharStatus class of the L2Object, is overwritten in classes that require a different CharStatus Type.
	 * 
	 * Removes the need for instanceof checks.
	 */
	public void initCharStatus() {
		_status = new CharStatus(this);
	}

	public final void setStatus(CharStatus value) {
		_status = value;
	}

	@Override
	public CharPosition getPosition() {
		return (CharPosition) _position;
	}

	@Override
	public void initPosition() {
		setObjectPosition(new CharPosition(this));
	}

	public CharTemplate getTemplate() {
		return _template;
	}

	/**
	 * 设置角色的模板<BR>
	 * <BR>
	 * 
	 * <B><U> Concept</U> :</B><BR>
	 * <BR>
	 * Each L2Character owns generic and static properties (ex : all Keltir have the same number of HP...). All of those properties are stored in a different template for each type of L2Character. Each template is loaded once in the server cache memory (reduce memory use). When a new instance of L2Character is spawned, server just create a link between the instance and the template This link is stored in <B>_template</B><BR>
	 * <BR>
	 * 
	 * <B><U> Assert </U> :</B><BR>
	 * <BR>
	 * <li>this instanceof L2Character</li><BR>
	 * <BR
	 */
	protected final void setTemplate(CharTemplate template) {
		_template = template;
	}

	/** Return the Title of the L2Character. */
	public final String getTitle() {
		return _title != null ? _title : "";
	}

	/** Set the Title of the L2Character. */
	public final void setTitle(String value) {
		if (value == null)
			_title = "";
		else
			_title = value.length() > 16 ? value.substring(0, 15) : value;
	}

	/** Task of AI notification */
	public class NotifyAITask implements Runnable {
		private final CtrlEvent _evt;

		NotifyAITask(CtrlEvent evt) {
			_evt = evt;
		}

		public void run() {
			try {
				getAI().notifyEvent(_evt, null);
			} catch (Exception e) {
				_log.error("Notify AI Task", getObjectId(), e);
			}
		}
	}

	/** Task lauching the function stopPvPFlag() */
	class PvPFlag implements Runnable {
		public PvPFlag() {

		}

		public void run() {
			try {
				if (System.currentTimeMillis() > getPvpFlagLasts()) {
					stopPvPFlag();
				} else if (System.currentTimeMillis() > (getPvpFlagLasts() - 20000)) {
					updatePvPFlag(2);
				} else {
					updatePvPFlag(1);
					// Start a new PvP timer check
					//checkPvPFlag();
				}
			} catch (Exception e) {
				_log.error("error in pvp flag task:", getObjectId(), e);
			}
		}
	}

	// =========================================================
	// Abnormal Effect - NEED TO REMOVE ONCE L2CHARABNORMALEFFECT IS COMPLETE
	// Data Field
	/** Map 32 bits (0x0000) containing all abnormal effect in progress */
	private int _AbnormalEffects;

	/**
	 * 角色的effects列表(包括buff，debuff)
	 */
	protected CharEffectList _effects = new CharEffectList(this);

	private int _SpecialEffects;

	// Method - Public
	/**
	 * Launch and add L2Effect (including Stack Group management) to L2Character and update client magic icon.<BR>
	 * <BR>
	 * 
	 * <B><U> Concept</U> :</B><BR>
	 * <BR>
	 * All active skills effects in progress on the L2Character are identified in ConcurrentHashMap(Integer,L2Effect) <B>_effects</B>. The Integer key of _effects is the L2Skill Identifier that has created the L2Effect.<BR>
	 * <BR>
	 * 
	 * Several same effect can't be used on a L2Character at the same time. Indeed, effects are not stackable and the last cast will replace the previous in progress. More, some effects belong to the same Stack Group (ex WindWald and Haste Potion). If 2 effects of a same group are used at the same time on a L2Character, only the more efficient (identified by its priority order) will be preserve.<BR>
	 * <BR>
	 * 
	 * <B><U> Actions</U> :</B><BR>
	 * <BR>
	 * <li>Add the L2Effect to the L2Character _effects</li> <li>If this effect doesn't belong to a Stack Group, add its Funcs to the Calculator set of the L2Character (remove the old one if necessary)</li> <li>If this effect has higher priority in its Stack Group, add its Funcs to the Calculator set of the L2Character (remove previous stacked effect Funcs if necessary)</li> <li>If this effect has NOT higher priority in its Stack Group, set the effect to Not In Use</li> <li>Update active skills in progress icons on player client</li><BR>
	 * 
	 */
	public void addEffect(L2Effect newEffect) {
		_effects.queueEffect(newEffect, false);
	}

	/**
	 * Stop and remove L2Effect (including Stack Group management) from L2Character and update client magic icon.<BR>
	 * <BR>
	 * 
	 * <B><U> Concept</U> :</B><BR>
	 * <BR>
	 * All active skills effects in progress on the L2Character are identified in ConcurrentHashMap(Integer,L2Effect) <B>_effects</B>. The Integer key of _effects is the L2Skill Identifier that has created the L2Effect.<BR>
	 * <BR>
	 * 
	 * Several same effect can't be used on a L2Character at the same time. Indeed, effects are not stackable and the last cast will replace the previous in progress. More, some effects belong to the same Stack Group (ex WindWald and Haste Potion). If 2 effects of a same group are used at the same time on a L2Character, only the more efficient (identified by its priority order) will be preserve.<BR>
	 * <BR>
	 * 
	 * <B><U> Actions</U> :</B><BR>
	 * <BR>
	 * <li>Remove Func added by this effect from the L2Character Calculator (Stop L2Effect)</li> <li>If the L2Effect belongs to a not empty Stack Group, replace theses Funcs by next stacked effect Funcs</li> <li>Remove the L2Effect from _effects of the L2Character</li> <li>Update active skills in progress icons on player client</li><BR>
	 * 
	 */
	public final void removeEffect(L2Effect effect) {
		_effects.queueEffect(effect, true);
	}

	/**
	 * Stop and remove the L2Effects corresponding to the L2Skill Identifier and update client magic icon.<BR>
	 * <BR>
	 * 
	 * <B><U> Concept</U> :</B><BR>
	 * <BR>
	 * All active skills effects in progress on the L2Character are identified in ConcurrentHashMap(Integer,L2Effect) <B>_effects</B>. The Integer key of _effects is the L2Skill Identifier that has created the L2Effect.<BR>
	 * <BR>
	 * 
	 * @param effectId
	 *            The L2Skill Identifier of the L2Effect to remove from _effects
	 * 
	 */
	public final void stopSkillEffects(int skillId) {
		_effects.stopSkillEffects(skillId);
	}

	/**
	 * Stop and remove all L2Effect of the selected type (ex : BUFF, DMG_OVER_TIME...) from the L2Character and update client magic icon.<BR>
	 * <BR>
	 * 
	 * <B><U> Concept</U> :</B><BR>
	 * <BR>
	 * All active skills effects in progress on the L2Character are identified in ConcurrentHashMap(Integer,L2Effect) <B>_effects</B>. The Integer key of _effects is the L2Skill Identifier that has created the L2Effect.<BR>
	 * <BR>
	 * 
	 * <B><U> Actions</U> :</B><BR>
	 * <BR>
	 * <li>Remove Func added by this effect from the L2Character Calculator (Stop L2Effect)</li> <li>Remove the L2Effect from _effects of the L2Character</li> <li>Update active skills in progress icons on player client</li><BR>
	 * <BR>
	 * 
	 * @param type
	 *            The type of effect to stop ((ex : BUFF, DMG_OVER_TIME...)
	 * 
	 */
	public final void stopEffects(L2EffectType type) {
		_effects.stopEffects(type);
	}

	/**
	 * Return all active skills effects in progress on the L2Character.<BR>
	 * <BR>
	 * 
	 * <B><U> Concept</U> :</B><BR>
	 * <BR>
	 * All active skills effects in progress on the L2Character are identified in <B>_effects</B>. The Integer key of _effects is the L2Skill Identifier that has created the effect.<BR>
	 * <BR>
	 * 
	 * @return A table containing all active skills effect in progress on the L2Character
	 * 
	 */
	public final L2Effect[] getAllEffects() {
		return _effects.getAllEffects();
	}

	/**
	 * 获得角色身上的 效果链
	 * 
	 * @return
	 */
	public final CharEffectList getEffectList() {
		return _effects;
	}

	/**
	 * Return L2Effect in progress on the L2Character corresponding to the L2Skill Identifier.<BR>
	 * <BR>
	 * 
	 * <B><U> Concept</U> :</B><BR>
	 * <BR>
	 * All active skills effects in progress on the L2Character are identified in <B>_effects</B>.
	 * 
	 * @param index
	 *            The L2Skill Identifier of the L2Effect to return from the _effects
	 * 
	 * @return The L2Effect corresponding to the L2Skill Identifier
	 * 
	 */
	public final L2Effect getFirstEffect(int index) {
		return _effects.getFirstEffect(index);
	}

	/**
	 * Return the first L2Effect in progress on the L2Character created by the L2Skill.<BR>
	 * <BR>
	 * 
	 * <B><U> Concept</U> :</B><BR>
	 * <BR>
	 * All active skills effects in progress on the L2Character are identified in <B>_effects</B>.
	 * 
	 * @param skill
	 *            The L2Skill whose effect must be returned
	 * 
	 * @return The first L2Effect created by the L2Skill
	 * 
	 */
	public final L2Effect getFirstEffect(L2Skill skill) {
		return _effects.getFirstEffect(skill);
	}

	/**
	 * Return the first L2Effect in progress on the L2Character corresponding to the Effect Type (ex : BUFF, STUN, ROOT...).<BR>
	 * <BR>
	 * 
	 * <B><U> Concept</U> :</B><BR>
	 * <BR>
	 * All active skills effects in progress on the L2Character are identified in <B>_effects</B>.
	 * 
	 * @param tp
	 *            The Effect Type of skills whose effect must be returned
	 * 
	 * @return The first L2Effect corresponding to the Effect Type
	 * 
	 */
	public final L2Effect getFirstEffect(L2EffectType tp) {
		return _effects.getFirstEffect(tp);
	}

	/**
	 * 返回影响这个角色的buff的数量.<BR>
	 * <BR>
	 * 
	 * @return 数量
	 */
	public int getBuffCount() {
		return _effects.getBuffCount();
	}

	/**
	 * 返回影响这个角色的唱歌跳舞的buff数量.<BR>
	 * <BR>
	 * 
	 * @return 数量
	 */
	public int getDanceCount() {
		return _effects.getDanceCount();
	}

	/**
	 * 停止所有的激活的技能效果.（包括buff和debuff）<BR>
	 * <BR>
	 */
	public void stopAllEffects() {
		_effects.stopAllEffects();
	}

	public void stopAllEffectsExceptThoseThatLastThroughDeath() {
		_effects.stopAllEffectsExceptThoseThatLastThroughDeath();
	}

	/**
	 * Active abnormal effects flags in the binary mask and send Server->Client UserInfo/CharInfo packet.<BR>
	 * <BR>
	 */
	public final void startAbnormalEffect(AbnormalEffect mask) {
		startAbnormalEffect(mask.getMask());
	}

	public final void startAbnormalEffect(int mask) {
		_AbnormalEffects |= mask;
		updateAbnormalEffect();
	}

	/**
	 * Active special effects flags in the binary mask and send Server->Client UserInfo/CharInfo packet.<BR>
	 * <BR>
	 */
	public final void startSpecialEffect(AbnormalEffect mask) {
		startSpecialEffect(mask.getMask());
	}

	public final void startSpecialEffect(int mask) {
		_SpecialEffects |= mask;
		updateAbnormalEffect();
	}

	/**
	 * Modify the abnormal effect map according to the mask.<BR>
	 * <BR>
	 */
	public final void stopAbnormalEffect(AbnormalEffect mask) {
		stopAbnormalEffect(mask.getMask());
	}

	public final void stopAbnormalEffect(int mask) {
		_AbnormalEffects &= ~mask;
		updateAbnormalEffect();
	}

	/**
	 * Modify the special effect map according to the mask.<BR>
	 * <BR>
	 */
	public final void stopSpecialEffect(AbnormalEffect mask) {
		stopSpecialEffect(mask.getMask());
	}

	public final void stopSpecialEffect(int mask) {
		_SpecialEffects &= ~mask;
		updateAbnormalEffect();
	}

	/**
	 * 激活异常状态混乱,通知角色AI and send Server->Client UserInfo/CharInfo packet.<BR>
	 * <BR>
	 */
	public final void startConfused() {
		setIsConfused(true);
		getAI().notifyEvent(CtrlEvent.EVT_CONFUSED);
		updateAbnormalEffect();
	}

	/**
	 * Stop a specified/all Confused abnormal L2Effect.<BR>
	 * <BR>
	 * 
	 * <B><U> Actions</U> :</B><BR>
	 * <BR>
	 * <li>Delete a specified/all (if effect=null) Confused abnormal L2Effect from L2Character and update client magic icon</li> <li>Set the abnormal effect flag _confused to False</li> <li>Notify the L2Character AI</li> <li>Send Server->Client UserInfo/CharInfo packet</li><BR>
	 * <BR>
	 * 
	 */
	public final void stopConfused(L2Effect effect) {
		if (effect == null)
			stopEffects(L2EffectType.CONFUSION);
		else
			removeEffect(effect);
		setIsConfused(false);
		getAI().notifyEvent(CtrlEvent.EVT_THINK);
		updateAbnormalEffect();
	}

	/**
	 * 激活异常状态恐惧，通知角色AI and send Server->Client UserInfo/CharInfo packet.<BR>
	 * <BR>
	 */
	public final void startFear() {
		setIsAfraid(true);
		getAI().notifyEvent(CtrlEvent.EVT_AFRAID);
		updateAbnormalEffect();
	}

	/**
	 * Stop a specified/all Fear abnormal L2Effect.<BR>
	 * <BR>
	 * 
	 * <B><U> Actions</U> :</B><BR>
	 * <BR>
	 * <li>Delete a specified/all (if effect=null) Fear abnormal L2Effect from L2Character and update client magic icon</li> <li>Set the abnormal effect flag _affraid to False</li> <li>Notify the L2Character AI</li> <li>Send Server->Client UserInfo/CharInfo packet</li><BR>
	 * <BR>
	 * 
	 */
	public final void stopFear(L2Effect effect) {
		if (effect == null)
			stopEffects(L2EffectType.FEAR);
		else
			removeEffect(effect);
		setIsAfraid(false);
		updateAbnormalEffect();
	}

	/**
	 * 激活异常状态魔法禁言，通知角色AI and send Server->Client UserInfo/CharInfo packet.<BR>
	 * <BR>
	 */
	public final void startMuted() {
		setIsMuted(true);
		getAI().notifyEvent(CtrlEvent.EVT_MUTED);
		updateAbnormalEffect();
	}

	/**
	 * Stop a specified/all Muted abnormal L2Effect.<BR>
	 * <BR>
	 * 
	 * <B><U> Actions</U> :</B><BR>
	 * <BR>
	 * <li>Delete a specified/all (if effect=null) Muted abnormal L2Effect from L2Character and update client magic icon</li> <li>Set the abnormal effect flag _muted to False</li> <li>Notify the L2Character AI</li> <li>Send Server->Client UserInfo/CharInfo packet</li><BR>
	 * <BR>
	 * 
	 */
	public final void stopMuted(L2Effect effect) {
		if (effect == null)
			stopEffects(L2EffectType.MUTE);
		else
			removeEffect(effect);
		setIsMuted(false);
		updateAbnormalEffect();
	}

	/**
	 * 激活异常状态物理禁言，通知角色AI and send Server->Client UserInfo/CharInfo packet.<BR>
	 * <BR>
	 */
	public final void startPsychicalMuted() {
		setIsPhysicalMuted(true);
		getAI().notifyEvent(CtrlEvent.EVT_MUTED);
		updateAbnormalEffect();
	}

	public final void stopPsychicalMuted(L2Effect effect) {
		if (effect == null)
			stopEffects(L2EffectType.PHYSICAL_MUTE);
		else
			removeEffect(effect);

		setIsPhysicalMuted(false);
		updateAbnormalEffect();
	}

	/**
	 * 激活异常状态盘绕，通知角色AI and send Server->Client UserInfo/CharInfo packet.<BR>
	 * <BR>
	 */
	public final void startRooted() {
		setIsRooted(true);
		getAI().notifyEvent(CtrlEvent.EVT_ROOTED);
		updateAbnormalEffect();
	}

	/**
	 * Stop a specified/all Root abnormal L2Effect.<BR>
	 * <BR>
	 * 
	 * <B><U> Actions</U> :</B><BR>
	 * <BR>
	 * <li>Delete a specified/all (if effect=null) Root abnormal L2Effect from L2Character and update client magic icon</li> <li>Set the abnormal effect flag _rooted to False</li> <li>Notify the L2Character AI</li> <li>Send Server->Client UserInfo/CharInfo packet</li><BR>
	 * <BR>
	 * 
	 */
	public final void stopRooting(L2Effect effect) {
		if (effect == null)
			stopEffects(L2EffectType.ROOT);
		else
			removeEffect(effect);
		setIsRooted(false);
		getAI().notifyEvent(CtrlEvent.EVT_THINK);
		updateAbnormalEffect();
	}

	/**
	 * 激活异常状态直到被攻击才解除的定身，通知角色AI and send Server->Client UserInfo/CharInfo packet.<BR>
	 * <BR>
	 */
	public final void startImmobileUntilAttacked() {
		setIsImmobileUntilAttacked(true);
		getAI().notifyEvent(CtrlEvent.EVT_SLEEPING);
		updateAbnormalEffect();
	}

	/**
	 * Stop immobilization until attacked abnormal L2Effect.<BR>
	 * <BR>
	 * 
	 * <B><U> Actions</U> :</B><BR>
	 * <BR>
	 * <li>Delete a specified/all (if effect=null) immobilization until attacked abnormal L2Effect from L2Character and update client magic icon</li> <li>Set the abnormal effect flag _muted to False</li> <li>Notify the L2Character AI</li> <li>Send Server->Client UserInfo/CharInfo packet</li><BR>
	 * <BR>
	 * 
	 */
	public final void stopImmobileUntilAttacked(L2Effect effect) {
		if (effect == null)
			stopEffects(L2EffectType.IMMOBILEUNTILATTACKED);
		else {
			removeEffect(effect);

			//			if (effect.getSkill().getNegateId().length != 0) {
			//				for (int i = 0; i < effect.getSkill().getNegateId().length; i++) {
			//					if (effect.getSkill().getNegateId()[i] != 0)
			//						stopSkillEffects(effect.getSkill().getNegateId()[i]);
			//				}
			//			}
		}
		setIsImmobileUntilAttacked(false);
		getAI().notifyEvent(CtrlEvent.EVT_THINK);
		updateAbnormalEffect();
	}

	/**
	 * 激活异常状态睡眠，通知角色AI and send Server->Client UserInfo/CharInfo packet.<BR>
	 * <BR>
	 */
	public final void startSleeping() {
		setIsSleeping(true);
		getAI().notifyEvent(CtrlEvent.EVT_SLEEPING);
		updateAbnormalEffect();
	}

	/**
	 * Stop a specified/all Sleep abnormal L2Effect.<BR>
	 * <BR>
	 * 
	 * <B><U> Actions</U> :</B><BR>
	 * <BR>
	 * <li>Delete a specified/all (if effect=null) Sleep abnormal L2Effect from L2Character and update client magic icon</li> <li>Set the abnormal effect flag _sleeping to False</li> <li>Notify the L2Character AI</li> <li>Send Server->Client UserInfo/CharInfo packet</li><BR>
	 * <BR>
	 * 
	 */
	public final void stopSleeping(L2Effect effect) {
		if (effect == null)
			stopEffects(L2EffectType.SLEEP);
		else
			removeEffect(effect);
		setIsSleeping(false);
		getAI().notifyEvent(CtrlEvent.EVT_THINK);
		updateAbnormalEffect();
	}

	/**
	 * 激活异常状态震惊，通知角色AI and send Server->Client UserInfo/CharInfo packet.<BR>
	 * <BR>
	 */
	public final void startStunning() {
		setIsStunned(true);
		getAI().notifyEvent(CtrlEvent.EVT_STUNNED);
		updateAbnormalEffect();
	}

	/**
	 * Stop a specified/all Stun abnormal L2Effect.<BR>
	 * <BR>
	 * 
	 * <B><U> Actions</U> :</B><BR>
	 * <BR>
	 * <li>Delete a specified/all (if effect=null) Stun abnormal L2Effect from L2Character and update client magic icon</li> <li>Set the abnormal effect flag _stuned to False</li> <li>Notify the L2Character AI</li> <li>Send Server->Client UserInfo/CharInfo packet</li><BR>
	 * <BR>
	 * 
	 */
	public final void stopStunning(L2Effect effect) {
		setIsStunned(false);
		if (effect == null)
			stopEffects(L2EffectType.STUN);
		else
			removeEffect(effect);
		getAI().notifyEvent(CtrlEvent.EVT_THINK);
		updateAbnormalEffect();
	}

	/**
	 * 激活异常状态瘫痪，通知角色AI and send Server->Client UserInfo/CharInfo packet.<BR>
	 * <BR>
	 */
	public final void startParalyze() {
		setIsParalyzed(true);
		getAI().notifyEvent(CtrlEvent.EVT_PARALYZED);
		updateAbnormalEffect();
	}

	public final void stopParalyze(L2Effect effect) {
		if (effect == null)
			stopEffects(L2EffectType.PARALYZE);
		else
			removeEffect(effect);
		setIsParalyzed(false);
		getAI().notifyEvent(CtrlEvent.EVT_THINK);
		updateAbnormalEffect();
	}

	/**
	 * 激活异常状态背叛，通知角色AI and send Server->Client UserInfo/CharInfo packet.<BR>
	 * <BR>
	 */
	public final void startBetray() {
		setIsBetrayed(true);
		getAI().notifyEvent(CtrlEvent.EVT_BETRAYED);
		updateAbnormalEffect();
	}

	/**
	 * 停止异常状态背叛 and send Server->Client UserInfo/CharInfo packet.<BR>
	 * <BR>
	 */
	public final void stopBetray() {
		stopEffects(L2EffectType.BETRAY);
		setIsBetrayed(false);
		updateAbnormalEffect();
	}

	/**
	 * Stop L2Effect: Transformation<BR>
	 * <BR>
	 * 
	 * <B><U> Actions</U> :</B><BR>
	 * <BR>
	 * <li>Remove Transformation Effect</li> <li>Notify the L2Character AI</li> <li>Send Server->Client UserInfo/CharInfo packet</li><BR>
	 * <BR>
	 * 
	 */
	public final void stopTransformation(L2Effect effect) {
		if (effect == null) {
			stopEffects(L2EffectType.TRANSFORMATION);
		} else {
			removeEffect(effect);
		}

		// if this is a player instance, then untransform, also set the transform_id column equal to 0 if not cursed.
		if (this instanceof FuzePcInstance) {
			if (((FuzePcInstance) this).getTransformation() != null) {
				((FuzePcInstance) this).untransform();
			}
		}

		getAI().notifyEvent(CtrlEvent.EVT_THINK);
		updateAbnormalEffect();
	}

	/**
	 * Not Implemented.<BR>
	 * <BR>
	 * 
	 * <B><U> Overridden in</U> :</B><BR>
	 * <BR>
	 * <li>L2NPCInstance</li> <li>L2PcInstance</li> <li>L2Summon</li><BR>
	 * <BR>
	 * 
	 */
	public abstract void updateAbnormalEffect();

	/**
	 * Update active skills in progress (In Use and Not In Use because stacked) icons on client.<BR>
	 * <BR>
	 * 
	 * <B><U> Concept</U> :</B><BR>
	 * <BR>
	 * All active skills effects in progress (In Use and Not In Use because stacked) are represented by an icon on the client.<BR>
	 * <BR>
	 * 
	 * <FONT COLOR=#FF0000><B> <U>Caution</U> : This method ONLY UPDATE the client of the player and not clients of all players in the party.</B></FONT><BR>
	 * <BR>
	 * 
	 */
	public final void updateEffectIcons() {
		updateEffectIcons(false);
	}

	/**
	 * Updates Effect Icons for this character(palyer/summon) and his party if any<BR>
	 * 
	 * Overridden in:<BR>
	 * L2PcInstance<BR>
	 * L2Summon<BR>
	 * 
	 * @param partyOnly
	 */
	public void updateEffectIcons(boolean partyOnly) {
		// overridden
	}

	// Property - Public
	/**
	 * Return a map of 16 bits (0x0000) containing all abnormal effect in progress for this L2Character.<BR>
	 * <BR>
	 * 
	 * <B><U> Concept</U> :</B><BR>
	 * <BR>
	 * In Server->Client packet, each effect is represented by 1 bit of the map (ex : BLEEDING = 0x0001 (bit 1), SLEEP = 0x0080 (bit 8)...). The map is calculated by applying a BINARY OR operation on each effect.<BR>
	 * <BR>
	 * 
	 * <B><U> Example of use </U> :</B><BR>
	 * <BR>
	 * <li>Server Packet : CharInfo, NpcInfo, NpcInfoPoly, UserInfo...</li><BR>
	 * <BR>
	 */
	public int getAbnormalEffect() {
		int ae = _AbnormalEffects;
		if (isStunned())
			ae |= AbnormalEffect.STUN.getMask();
		if (isRooted())
			ae |= AbnormalEffect.ROOT.getMask();
		if (isSleeping())
			ae |= AbnormalEffect.SLEEP.getMask();
		if (isConfused())
			ae |= AbnormalEffect.CONFUSED.getMask();
		if (isMuted())
			ae |= AbnormalEffect.MUTED.getMask();
		if (isPhysicalMuted())
			ae |= AbnormalEffect.MUTED.getMask();
		return ae;
	}

	/**
	 * Return a map of 32 bits (0x00000000) containing all special effect in progress for this L2Character.<BR>
	 * <BR>
	 * 
	 * <B><U> Concept</U> :</B><BR>
	 * <BR>
	 * In Server->Client packet, each effect is represented by 1 bit of the map (ex : INVULNERABLE = 0x0001 (bit 1), PINK_AFFRO = 0x0020 (bit 6)...). The map is calculated by applying a BINARY OR operation on each effect.<BR>
	 * <BR>
	 * 
	 * <B><U> Example of use </U> :</B><BR>
	 * <BR>
	 * <li>Server Packet : CharInfo, UserInfo...</li><BR>
	 * <BR>
	 */
	public int getSpecialEffect() {
		int se = _SpecialEffects;
		if (isStunned())
			se |= AbnormalEffect.S_AIR_STUN.getMask();
		if (isRooted())
			se |= AbnormalEffect.S_AIR_ROOT.getMask();
		return se;
	}

	/**
	 * Add a Func to the Calculator set of the L2Character.<BR>
	 * <BR>
	 * 
	 * <B><U> Concept</U> :</B><BR>
	 * <BR>
	 * A L2Character owns a table of Calculators called <B>_calculators</B>. Each Calculator (a calculator per state) own a table of Func object. A Func object is a mathematic function that permit to calculate the modifier of a state (ex : REGENERATE_HP_RATE...). To reduce cache memory use, L2NPCInstances who don't have skills share the same Calculator set called <B>NPC_STD_CALCULATOR</B>.<BR>
	 * <BR>
	 * 
	 * That's why, if a L2NPCInstance is under a skill/spell effect that modify one of its state, a copy of the NPC_STD_CALCULATOR must be create in its _calculators before addind new Func object.<BR>
	 * <BR>
	 * 
	 * <B><U> Actions</U> :</B><BR>
	 * <BR>
	 * <li>If _calculators is linked to NPC_STD_CALCULATOR, create a copy of NPC_STD_CALCULATOR in _calculators</li> <li>Add the Func object to _calculators</li><BR>
	 * <BR>
	 * 
	 * @param f
	 *            The Func object to add to the Calculator corresponding to the state affected
	 */
	public final void addStatFunc(Func f) {
		if (f == null)
			return;

		synchronized (_calculators) {
			// Check if Calculator set is linked to the standard Calculator set of NPC
			if (_calculators == NPC_STD_CALCULATOR) {
				// Create a copy of the standard NPC Calculator set
				_calculators = new Calculator[Stats.NUM_STATS];

				for (int i = 0; i < Stats.NUM_STATS; i++) {
					if (NPC_STD_CALCULATOR[i] != null)
						_calculators[i] = new Calculator(NPC_STD_CALCULATOR[i]);
				}
			}

			// Select the Calculator of the affected state in the Calculator set
			int stat = f.stat.ordinal();

			if (_calculators[stat] == null)
				_calculators[stat] = new Calculator();

			// Add the Func to the calculator corresponding to the state
			_calculators[stat].addFunc(f);
		}
	}

	/**
	 * Add a list of Funcs to the Calculator set of the L2Character.<BR>
	 * <BR>
	 * 
	 * <B><U> Concept</U> :</B><BR>
	 * <BR>
	 * A L2Character owns a table of Calculators called <B>_calculators</B>. Each Calculator (a calculator per state) own a table of Func object. A Func object is a mathematic function that permit to calculate the modifier of a state (ex : REGENERATE_HP_RATE...). <BR>
	 * <BR>
	 * 
	 * <FONT COLOR=#FF0000><B> <U>Caution</U> : This method is ONLY for L2PcInstance</B></FONT><BR>
	 * <BR>
	 * 
	 * <B><U> Example of use </U> :</B><BR>
	 * <BR>
	 * <li>Equip an item from inventory</li> <li>Learn a new passive skill</li> <li>Use an active skill</li><BR>
	 * <BR>
	 * 
	 * @param funcs
	 *            The list of Func objects to add to the Calculator corresponding to the state affected
	 */
	public final void addStatFuncs(Func[] funcs) {
		FastList<Stats> modifiedStats = new FastList<Stats>();

		for (Func f : funcs) {
			modifiedStats.add(f.stat);
			addStatFunc(f);
		}
		broadcastModifiedStats(modifiedStats);
	}

	/**
	 * Remove a Func from the Calculator set of the L2Character.<BR>
	 * <BR>
	 * 
	 * <B><U> Concept</U> :</B><BR>
	 * <BR>
	 * A L2Character owns a table of Calculators called <B>_calculators</B>. Each Calculator (a calculator per state) own a table of Func object. A Func object is a mathematic function that permit to calculate the modifier of a state (ex : REGENERATE_HP_RATE...). To reduce cache memory use, L2NPCInstances who don't have skills share the same Calculator set called <B>NPC_STD_CALCULATOR</B>.<BR>
	 * <BR>
	 * 
	 * That's why, if a L2NPCInstance is under a skill/spell effect that modify one of its state, a copy of the NPC_STD_CALCULATOR must be create in its _calculators before addind new Func object.<BR>
	 * <BR>
	 * 
	 * <B><U> Actions</U> :</B><BR>
	 * <BR>
	 * <li>Remove the Func object from _calculators</li><BR>
	 * <BR>
	 * <li>If L2Character is a L2NPCInstance and _calculators is equal to NPC_STD_CALCULATOR, free cache memory and just create a link on NPC_STD_CALCULATOR in _calculators</li><BR>
	 * <BR>
	 * 
	 * @param f
	 *            The Func object to remove from the Calculator corresponding to the state affected
	 */
	public final void removeStatFunc(Func f) {
		if (f == null)
			return;

		// Select the Calculator of the affected state in the Calculator set
		int stat = f.stat.ordinal();

		synchronized (_calculators) {
			if (_calculators[stat] == null)
				return;

			// Remove the Func object from the Calculator
			_calculators[stat].removeFunc(f);

			if (_calculators[stat].size() == 0)
				_calculators[stat] = null;

			// If possible, free the memory and just create a link on NPC_STD_CALCULATOR
			if (this instanceof L2Npc) {
				int i = 0;
				for (; i < Stats.NUM_STATS; i++) {
					if (!Calculator.equalsCals(_calculators[i], NPC_STD_CALCULATOR[i]))
						break;
				}

				if (i >= Stats.NUM_STATS)
					_calculators = NPC_STD_CALCULATOR;
			}
		}
	}

	/**
	 * Remove a list of Funcs from the Calculator set of the L2PcInstance.<BR>
	 * <BR>
	 * 
	 * <B><U> Concept</U> :</B><BR>
	 * <BR>
	 * A L2Character owns a table of Calculators called <B>_calculators</B>. Each Calculator (a calculator per state) own a table of Func object. A Func object is a mathematic function that permit to calculate the modifier of a state (ex : REGENERATE_HP_RATE...). <BR>
	 * <BR>
	 * 
	 * <FONT COLOR=#FF0000><B> <U>Caution</U> : This method is ONLY for L2PcInstance</B></FONT><BR>
	 * <BR>
	 * 
	 * <B><U> Example of use </U> :</B><BR>
	 * <BR>
	 * <li>Unequip an item from inventory</li> <li>Stop an active skill</li><BR>
	 * <BR>
	 * 
	 * @param funcs
	 *            The list of Func objects to add to the Calculator corresponding to the state affected
	 */
	public final void removeStatFuncs(Func[] funcs) {

		FastList<Stats> modifiedStats = new FastList<Stats>();

		for (Func f : funcs) {
			modifiedStats.add(f.stat);
			removeStatFunc(f);
		}

		broadcastModifiedStats(modifiedStats);

	}

	/**
	 * Remove all Func objects with the selected owner from the Calculator set of the L2Character.<BR>
	 * <BR>
	 * 
	 * <B><U> Concept</U> :</B><BR>
	 * <BR>
	 * A L2Character owns a table of Calculators called <B>_calculators</B>. Each Calculator (a calculator per state) own a table of Func object. A Func object is a mathematic function that permit to calculate the modifier of a state (ex : REGENERATE_HP_RATE...). To reduce cache memory use, L2NPCInstances who don't have skills share the same Calculator set called <B>NPC_STD_CALCULATOR</B>.<BR>
	 * <BR>
	 * 
	 * That's why, if a L2NPCInstance is under a skill/spell effect that modify one of its state, a copy of the NPC_STD_CALCULATOR must be create in its _calculators before addind new Func object.<BR>
	 * <BR>
	 * 
	 * <B><U> Actions</U> :</B><BR>
	 * <BR>
	 * <li>Remove all Func objects of the selected owner from _calculators</li><BR>
	 * <BR>
	 * <li>If L2Character is a L2NPCInstance and _calculators is equal to NPC_STD_CALCULATOR, free cache memory and just create a link on NPC_STD_CALCULATOR in _calculators</li><BR>
	 * <BR>
	 * 
	 * <B><U> Example of use </U> :</B><BR>
	 * <BR>
	 * <li>Unequip an item from inventory</li> <li>Stop an active skill</li><BR>
	 * <BR>
	 * 
	 * @param owner
	 *            The Object(Skill, Item...) that has created the effect
	 */
	public final void removeStatsOwner(Object owner) {

		FastList<Stats> modifiedStats = null;

		int i = 0;
		// Go through the Calculator set
		synchronized (_calculators) {
			for (Calculator calc : _calculators) {
				if (calc != null) {
					// Delete all Func objects of the selected owner
					if (modifiedStats != null)
						modifiedStats.addAll(calc.removeOwner(owner));
					else
						modifiedStats = calc.removeOwner(owner);

					if (calc.size() == 0)
						_calculators[i] = null;
				}
				i++;
			}

			// If possible, free the memory and just create a link on NPC_STD_CALCULATOR
			if (this instanceof L2Npc) {
				i = 0;
				for (; i < Stats.NUM_STATS; i++) {
					if (!Calculator.equalsCals(_calculators[i], NPC_STD_CALCULATOR[i]))
						break;
				}

				if (i >= Stats.NUM_STATS)
					_calculators = NPC_STD_CALCULATOR;
			}

			if (owner instanceof L2Effect && !((L2Effect) owner).preventExitUpdate)
				broadcastModifiedStats(modifiedStats);
		}
	}

	private void broadcastModifiedStats(FastList<Stats> stats) {
		if (stats == null || stats.isEmpty())
			return;

		boolean broadcastFull = false;
		boolean otherStats = false;
		StatusUpdate su = null;

		for (Stats stat : stats) {
			if (this instanceof L2Summon && ((L2Summon) this).getOwner() != null) {
				((L2Summon) this).updateAndBroadcastStatus(1);
				break;

			} else if (stat == Stats.POWER_ATTACK_SPEED) {
				if (su == null)
					su = new StatusUpdate(getObjectId());
				su.addAttribute(StatusUpdate.P_ATK_SPD, getPAtkSpd());
			} else if (stat == Stats.RUN_SPEED) {
				broadcastFull = true;
			} else
				otherStats = true;
		}

		if (this instanceof FuzePcInstance) {
			if (broadcastFull)
				((FuzePcInstance) this).updateAndBroadcastStatus(2);
			else {
				if (otherStats) {
					((FuzePcInstance) this).updateAndBroadcastStatus(1);
					if (su != null) {
						Collection<FuzePcInstance> plrs = getKnownList().getKnownPlayers().values();
						//synchronized (getKnownList().getKnownPlayers())
						{
							for (FuzePcInstance player : plrs) {
								try {
									player.sendPacket(su);
								} catch (NullPointerException e) {
								}
							}
						}
					}
				} else if (su != null)
					broadcastPacket(su);
			}
		} else if (this instanceof L2Npc) {
			if (broadcastFull) {
				Collection<FuzePcInstance> plrs = getKnownList().getKnownPlayers().values();

				//				for (L2PcInstance player : plrs) {
				//					if (getRunSpeed() == 0)
				//						player.sendPacket(new ServerObjectInfo((L2Npc) this, player));
				//					else
				//						player.sendPacket(new AbstractNpcInfo.NpcInfo((L2Npc) this, player));
				//				}

			} else if (su != null)
				broadcastPacket(su);
		} else if (su != null)
			broadcastPacket(su);
	}

	public final int getXdestination() {
		MoveData m = _move;

		if (m != null)
			return m.aimX;

		return getX();
	}

	/**
	 * Return the Y destination of the L2Character or the Y position if not in movement.<BR>
	 * <BR>
	 */
	public final int getYdestination() {
		MoveData m = _move;

		if (m != null)
			return m.aimY;

		return getY();
	}

	/**
	 * 是否角色处于战斗状态.<BR>
	 * <BR>
	 */
	public boolean isInCombat() {
		return (getAI().getAttackTarget() != null || getAI().isAutoAttacking());
	}

	/**
	 * 是否角色正在攻击.<BR>
	 * <BR>
	 */
	public boolean isAttackingNow() {
		return _attackEndTime > GameTimeController.getGameTicks();
	}

	public void revalidateZone(boolean force) {
		//		 Cannot validate if not in  a world region (happens during teleport)
		if (getScene() == null)
			return;

		// This function is called too often from movement code
		if (force)
			_zoneValidateCounter = 4;
		else {
			_zoneValidateCounter--;
			if (_zoneValidateCounter < 0)
				_zoneValidateCounter = 4;
			else
				return;
		}

		getScene().revalidateZones(this);
	}

	/**
	 * 设置目标对象 (把L2Object付给_target，<del>并互相增加到对方的knownlist)</del>.<BR>
	 * <BR>
	 * 
	 * <B><U> Concept</U> :</B><BR>
	 * <BR>
	 * The L2Object (including L2Character) targeted is identified in <B>_target</B> of the L2Character<BR>
	 * <BR>
	 * 
	 * <B><U> Actions</U> :</B><BR>
	 * <BR>
	 * <li>Set the _target of L2Character to L2Object</li> <li>If necessary, add L2Object to _knownObject of the L2Character</li> <li>If necessary, add L2Character to _KnownObject of the L2Object</li> <li>If object==null, cancel Attak or Cast</li><BR>
	 * <BR>
	 * 
	 * <B><U> Overridden in </U> :</B><BR>
	 * <BR>
	 * <li>L2PcInstance : Remove the L2PcInstance from the old target _statusListener and add it to the new target if it was a L2Character</li><BR>
	 * <BR>
	 * 
	 * @param object
	 *            L2object to target
	 * 
	 */
	public void setTarget(L2Object object) {
		if (object != null && !object.isVisible())
			object = null;
		_target = object;
	}

	/**
	 * 返回目标的id或是 -1.<BR>
	 * <BR>
	 */
	public final long getTargetId() {
		if (_target != null) {
			return _target.getObjectId();
		}

		return -1;
	}

	/**
	 * 返回目标对象或 null.<BR>
	 * <BR>
	 */
	public final L2Object getTarget() {
		return _target;
	}

	/**
	 * This class group all mouvement data.<BR>
	 * <BR>
	 * 
	 * <B><U> Data</U> :</B><BR>
	 * <BR>
	 * <li>_moveTimestamp : Last time position update</li> <li>_xDestination, _yDestination, _zDestination : Position of the destination</li> <li>_xMoveFrom, _yMoveFrom, _zMoveFrom : Position of the origin</li> <li>_moveStartTime : Start time of the movement</li> <li>_ticksToMove : Nb of ticks between the start and the destination</li> <li>_xSpeedTicks, _ySpeedTicks : Speed in unit/ticks</li><BR>
	 * <BR>
	 * 
	 * */
	public static class MoveData {

		public Node geoPath;

		public int aimX;

		public int aimY;

		public int nextX;

		public int nextY;
	}

	/**
	 * 为移动的动作计算移动数据，并且增加L2Character到GameTimeController中的movingObjects里（此方法仅仅被AIAccessor调用）.<BR>
	 * <BR>
	 * 
	 * <B><U> Concept</U> :</B><BR>
	 * <BR>
	 * At the beginning of the move action, all properties of the movement are stored in the MoveData object called <B>_move</B> of the L2Character. The position of the start point and of the destination permit to estimated in function of the movement speed the time to achieve the destination.<BR>
	 * <BR>
	 * All L2Character in movement are identified in <B>movingObjects</B> of GameTimeController that will call the updatePosition method of those L2Character each 0.1s.<BR>
	 * <BR>
	 * 
	 * <B><U> Actions</U> :</B><BR>
	 * <BR>
	 * <li>Get current position of the L2Character</li> <li>Calculate distance (dx,dy) between current position and destination including offset</li> <li>Create and Init a MoveData object</li> <li>Set the L2Character _move object to MoveData object</li> <li>Add the L2Character to movingObjects of the GameTimeController</li> <li>Create a task to notify the AI that L2Character arrives at a check point of the movement</li><BR>
	 * <BR>
	 * 
	 * <FONT COLOR=#FF0000><B> <U>Caution</U> : This method DOESN'T send Server->Client packet MoveToPawn/CharMoveToLocation </B></FONT><BR>
	 * <BR>
	 * 
	 * <B><U> Example of use </U> :</B><BR>
	 * <BR>
	 * <li>AI : onIntentionMoveTo(L2CharPosition), onIntentionPickUp(L2Object), onIntentionInteract(L2Object)</li> <li>FollowTask</li><BR>
	 * <BR>
	 * 
	 * @param x
	 *            目标的X坐标
	 * @param y
	 *            目标的Y坐标
	 * @param offset
	 *            The size of the interaction area of the L2Character targeted
	 * 
	 */
	protected void moveToLocation(int x, int y, int offset) {
		//获得这个角色的速度
		int speed = getStat().getRunSpeed();
		if (speed <= 0 || isMovementDisabled())
			return;

		// 获得角色当前的X，Y坐标
		int curX = getX();
		int curY = getY();

		Scene scn = getPosition().getScene();
		MoveData md = _move;
		Location loca = null;

		//检查能够到达的最终点是哪里
		loca = scn.getGeoData().moveCheck(curX, curY, x, y);
		// 创建一个移动数据对象++
		md.geoPath = PathFinding.getInstance().findPath(scn.get_id(), curX, curY, loca.getX(), loca.getY());

		if (md.geoPath != null) {//如果有路径下一个点是自己所在的tile的+8，+12偏移上
			AbstractNodeLoc loc = md.geoPath.getLoc();
			md.nextX = loc.getX() + 8;
			md.nextY = loc.getY() + 12;
			md.aimX = loca.getX();
			md.aimY = loca.getY();
		} else {//如果没有路径下一个点是自己所在的位置，原地走（不动）
			md.nextX = curX;
			md.nextY = curY;
			md.aimX = curX;
			md.aimY = curY;
		}

	}

	public boolean moveToNextRoutePoint() {
		MoveData md = _move;
		// Get the Move Speed of the L2Charcater
		int speed = getStat().getRunSpeed();
		if (speed <= 0 || isMovementDisabled()) {
			return false;
		}

		if (getX() == md.aimX && getY() == md.aimY) {
			return false;
		}
		if (md.geoPath != null)
			md.geoPath = md.geoPath.getParent();

		if (md.geoPath == null) {//说明已经到目的点的块上了，再次修正到最终的目的点
			md.nextX = md.aimX;
			md.nextY = md.aimY;
			return true;
		}

		AbstractNodeLoc loc = md.geoPath.getLoc();
		md.nextX = loc.getX() + 8;
		md.nextY = loc.getY() + 12;

		return true;
	}

	/**
	 * Update the position of the L2Character during a movement and return True if the movement is finished.<BR>
	 * <BR>
	 * 
	 */
	public boolean updatePosition() {
		// Get movement data
		MoveData m = _move;

		int x = getX();
		int y = getY();

		CharPosition p = getPosition();

		int speed = getStat().getRunSpeed();
		int cood;
		if (m.nextY < y) {
			cood = y - speed;
			if (cood < m.nextY)
				cood = m.nextY;
			p.setXY(x, cood);
		} else if (m.nextX > x) {
			cood = x + speed;
			if (cood > m.nextX)
				cood = m.nextX;
			p.setXY(cood, y);
		} else if (m.nextX < x) {
			cood = x - speed;
			if (cood < m.nextX)
				cood = m.nextX;
			p.setXY(cood, y);
		} else if (m.nextY > y) {
			cood = y + speed;
			if (cood > m.nextY)
				cood = m.nextY;
			p.setXY(x, cood);
		}

		boolean updata = p.getX() == m.nextX && p.getY() == m.nextY;

		if (updata)
			revalidateZone(false);

		return updata;
	}

	/**
	 * Return the distance between the current position of the L2Character and the target (x,y).<BR>
	 * <BR>
	 * 
	 * @param x
	 *            X position of the target
	 * @param y
	 *            Y position of the target
	 * @return the plan distance
	 */
	public final int getDistance(int x, int y) {
		return Util.FastDistance2D(x, y, getX(), getY());
	}

	/**
	 * Check if this object is inside the given radius around the given object. Warning: doesn't cover collision radius!<BR>
	 * <BR>
	 * 
	 * @param object
	 *            the target
	 * @param radius
	 *            the radius around the target
	 * @param checkZ
	 *            should we check Z axis also
	 * @param strictCheck
	 *            true if (distance < radius), false if (distance <= radius)
	 * @return true is the L2Character is inside the radius.
	 * 
	 * @see labox.innovation.gameserver.model.L2Character.isInsideRadius(int x, int y, int z, int radius, boolean checkZ, boolean strictCheck)
	 */
	public final boolean isInsideRadius(L2Object object, int radius) {
		return isInsideRadius(object.getX(), object.getY(), radius);
	}

	/**
	 * Check if this object is inside the given radius around the given point.<BR>
	 * <BR>
	 * 
	 * @param x
	 *            X position of the target
	 * @param y
	 *            Y position of the target
	 * @param radius
	 *            the radius around the target
	 * @param checkZ
	 *            should we check Z axis also
	 * @param strictCheck
	 *            true if (distance < radius), false if (distance <= radius)
	 * @return true is the L2Character is inside the radius.
	 */
	public final boolean isInsideRadius(int x, int y, int radius) {
		return Util.FastDistance2D(x, y, getX(), getY()) < radius;
	}

	/**
	 * Retun True if arrows are available.<BR>
	 * <BR>
	 * 
	 * <B><U> Overridden in </U> :</B><BR>
	 * <BR>
	 * <li>L2PcInstance</li><BR>
	 * <BR>
	 * 
	 */
	protected boolean checkAndEquipArrows() {
		return true;
	}

	/**
	 * Retun True if bolts are available.<BR>
	 * <BR>
	 * 
	 * <B><U> Overridden in </U> :</B><BR>
	 * <BR>
	 * <li>L2PcInstance</li><BR>
	 * <BR>
	 * 
	 */
	protected boolean checkAndEquipBolts() {
		return true;
	}

	/**
	 * Add Exp and Sp to the L2Character.<BR>
	 * <BR>
	 * 
	 * <B><U> Overridden in </U> :</B><BR>
	 * <BR>
	 * <li>L2PcInstance</li> <li>L2PetInstance</li><BR>
	 * <BR>
	 * 
	 */
	public void addExp(int addToExp) {

		// Dummy method (overridden by players and pets)
	}

	/**
	 * Return the active weapon instance (always equiped in the right hand).<BR>
	 * <BR>
	 * 
	 * <B><U> Overridden in </U> :</B><BR>
	 * <BR>
	 * <li>L2PcInstance</li><BR>
	 * <BR>
	 * 
	 */
	public abstract L2ItemInstance getActiveWeaponInstance();

	/**
	 * Return the active weapon item (always equiped in the right hand).<BR>
	 * <BR>
	 * 
	 * <B><U> Overridden in </U> :</B><BR>
	 * <BR>
	 * <li>L2PcInstance</li><BR>
	 * <BR>
	 * 
	 */
	public abstract L2Weapon getActiveWeaponItem();

	/**
	 * Return the secondary weapon instance (always equiped in the left hand).<BR>
	 * <BR>
	 * 
	 * <B><U> Overridden in </U> :</B><BR>
	 * <BR>
	 * <li>L2PcInstance</li><BR>
	 * <BR>
	 * 
	 */
	public abstract L2ItemInstance getSecondaryWeaponInstance();

	/**
	 * Return the secondary weapon item (always equiped in the left hand).<BR>
	 * <BR>
	 * 
	 * <B><U> Overridden in </U> :</B><BR>
	 * <BR>
	 * <li>L2PcInstance</li><BR>
	 * <BR>
	 * 
	 */
	public abstract L2Weapon getSecondaryWeaponItem();

	/**
	 * Break an attack and send Server->Client ActionFailed packet and a System Message to the L2Character.<BR>
	 * <BR>
	 */
	public void breakAttack() {
		if (isAttackingNow()) {
			// Abort the attack of the L2Character and send Server->Client ActionFailed packet

			if (this instanceof FuzePcInstance) {
				//TODO Remove sendPacket because it's always done in abortAttack
				sendPacket(ActionFailed.STATIC_PACKET);
				// Send a system message
				sendPacket(new SystemMessage(SystemMessageId.ATTACK_FAILED));
			}
		}
	}

	/**
	 * Reduce the arrow number of the L2Character.<BR>
	 * <BR>
	 * 
	 * <B><U> Overridden in </U> :</B><BR>
	 * <BR>
	 * <li>L2PcInstance</li><BR>
	 * <BR>
	 * 
	 */
	protected void reduceArrowCount(boolean bolts) {
		// default is to do nothing
	}

	/**
	 * Manage Forced attack (shift + select target).<BR>
	 * <BR>
	 * 
	 * <B><U> Actions</U> :</B><BR>
	 * <BR>
	 * <li>If L2Character or target is in a town area, send a system message TARGET_IN_PEACEZONE a Server->Client packet ActionFailed</li> <li>If target is confused, send a Server->Client packet ActionFailed</li> <li>If L2Character is a L2ArtefactInstance, send a Server->Client packet ActionFailed</li> <li>Send a Server->Client packet MyTargetSelected to start attack and Notify AI with AI_INTENTION_ATTACK</li><BR>
	 * <BR>
	 * 
	 * @param player
	 *            The L2PcInstance to attack
	 * 
	 */
	@Override
	public void onForcedAttack(FuzePcInstance player) {
		if (isInsidePeaceZone(player)) {
			// If L2Character or target is in a peace zone, send a system message TARGET_IN_PEACEZONE a Server->Client packet ActionFailed
			player.sendPacket(new SystemMessage(SystemMessageId.TARGET_IN_PEACEZONE));
			player.sendPacket(ActionFailed.STATIC_PACKET);
			return;
		}
		if (player.getTarget() != null && !player.getTarget().isAttackable() && !player.getAccessLevel().allowPeaceAttack()) {
			// If target is not attackable, send a Server->Client packet ActionFailed
			player.sendPacket(ActionFailed.STATIC_PACKET);
			return;
		}
		if (player.isConfused()) {
			// If target is confused, send a Server->Client packet ActionFailed
			player.sendPacket(ActionFailed.STATIC_PACKET);
			return;
		}
		// GeoData Los Check or dz > 1000
		//		if (!GeoData.getInstance().canSeeTarget(player, this)) {
		//			player.sendPacket(new SystemMessage(SystemMessageId.CANT_SEE_TARGET));
		//			player.sendPacket(ActionFailed.STATIC_PACKET);
		//			return;
		//		}
		// Notify AI with AI_INTENTION_ATTACK
		player.getAI().setIntention(CtrlIntention.AI_INTENTION_ATTACK, this);
	}

	/**
	 * 如果在安全区则返回true.<BR>
	 * <BR>
	 */
	public boolean isInsidePeaceZone(FuzePcInstance attacker) {
		return isInsidePeaceZone(attacker, this);
	}

	/**
	 * 如果在安全区则返回true.<BR>
	 * <BR>
	 */
	public boolean isInsidePeaceZone(FuzePcInstance attacker, L2Object target) {
		return (!attacker.getAccessLevel().allowPeaceAttack() && isInsidePeaceZone((L2Object) attacker, target));
	}

	/**
	 * 如果在安全区则返回true.<BR>
	 * <BR>
	 */
	public boolean isInsidePeaceZone(L2Object attacker, L2Object target) {
		if (target == null)
			return false;

		//只要有一个不是能玩的角色就不在安全区域
		if (target.getActingPlayer() == null || attacker.getActingPlayer() == null)
			return false;

		//临时检查场景的PK状态
		if (attacker.getScene().isCanPk())
			return false;
		else
			return true;
		//如果是在PVP副本就不在安全区域
//		if (InstanceManager.getInstance().getInstance(this.getInstanceId()).isPvPInstance())
//			return false;
//
//		if (attacker instanceof L2Character && target instanceof L2Character) {
//			return (((L2Character) target).isInsideZone(ZONE_PEACE) || ((L2Character) attacker).isInsideZone(ZONE_PEACE));
//		}
//
//		return true;
	}

	/**
	 * return true if this character is inside an active grid.
	 */
	public Boolean isInActiveRegion() {
		SceneRegion region = getPosition().getSceneRegion();
		return region != null && region.isActive();
	}

	/**
	 * Return True if the L2Character has a Party in progress.<BR>
	 * <BR>
	 */
	public boolean isInParty() {
		return false;
	}

	/**
	 * Return the L2Party object of the L2Character.<BR>
	 * <BR>
	 */
	public L2Party getParty() {
		return null;
	}

	/**
	 * Return True if the L2Character use a dual weapon.<BR>
	 * <BR>
	 */
	public boolean isUsingDualWeapon() {
		return false;
	}

	/**
	 * Add a skill to the L2Character _skills and its Func objects to the calculator set of the L2Character.<BR>
	 * <BR>
	 * 
	 * <B><U> Concept</U> :</B><BR>
	 * <BR>
	 * All skills own by a L2Character are identified in <B>_skills</B><BR>
	 * <BR>
	 * 
	 * <B><U> Actions</U> :</B><BR>
	 * <BR>
	 * <li>Replace oldSkill by newSkill or Add the newSkill</li> <li>If an old skill has been replaced, remove all its Func objects of L2Character calculator set</li> <li>Add Func objects of newSkill to the calculator set of the L2Character</li><BR>
	 * <BR>
	 * 
	 * <B><U> Overridden in </U> :</B><BR>
	 * <BR>
	 * <li>L2PcInstance : Save update in the character_skills table of the database</li><BR>
	 * <BR>
	 * 
	 * @param newSkill
	 *            The L2Skill to add to the L2Character
	 * 
	 * @return The L2Skill replaced or null if just added a new L2Skill
	 * 
	 */
	public L2Skill addSkill(L2Skill newSkill) {
		L2Skill oldSkill = null;

		if (newSkill != null) {
			// Replace oldSkill by newSkill or Add the newSkill
			oldSkill = _skills.put(newSkill.getId(), newSkill);

			// If an old skill has been replaced, remove all its Func objects
			if (oldSkill != null) {
				// if skill came with another one, we should delete the other one too.
				if ((oldSkill.triggerAnotherSkill())) {
					removeSkill(oldSkill.getTriggeredId(), true);
				}
				removeStatsOwner(oldSkill);
			}
			// Add Func objects of newSkill to the calculator set of the L2Character
			addStatFuncs(newSkill.getStatFuncs(null, this));

			if (oldSkill != null && _chanceSkills != null) {
				removeChanceSkill(oldSkill.getId());
			}
			if (newSkill.isChance()) {
				addChanceSkill(newSkill);
			}
		}

		return oldSkill;
	}

	/**
	 * 获得角色身上指定id的技能
	 * 
	 * @param skillId
	 * @return
	 */
	public L2Skill getSkill(int skillId) {
		return _skills.get(skillId);
	}

	/**
	 * Remove a skill from the L2Character and its Func objects from calculator set of the L2Character.<BR>
	 * <BR>
	 * 
	 * <B><U> Concept</U> :</B><BR>
	 * <BR>
	 * All skills own by a L2Character are identified in <B>_skills</B><BR>
	 * <BR>
	 * 
	 * <B><U> Actions</U> :</B><BR>
	 * <BR>
	 * <li>Remove the skill from the L2Character _skills</li> <li>Remove all its Func objects from the L2Character calculator set</li><BR>
	 * <BR>
	 * 
	 * <B><U> Overridden in </U> :</B><BR>
	 * <BR>
	 * <li>L2PcInstance : Save update in the character_skills table of the database</li><BR>
	 * <BR>
	 * 
	 * @param skill
	 *            The L2Skill to remove from the L2Character
	 * 
	 * @return The L2Skill removed
	 * 
	 */
	public L2Skill removeSkill(L2Skill skill) {
		if (skill == null)
			return null;

		return removeSkill(skill.getId(), true);
	}

	public L2Skill removeSkill(L2Skill skill, boolean cancelEffect) {
		if (skill == null)
			return null;

		// Remove the skill from the L2Character _skills
		return removeSkill(skill.getId(), cancelEffect);
	}

	public L2Skill removeSkill(int skillId) {
		return removeSkill(skillId, true);
	}

	public L2Skill removeSkill(int skillId, boolean cancelEffect) {
		// Remove the skill from the L2Character _skills
		L2Skill oldSkill = _skills.remove(skillId);
		// Remove all its Func objects from the L2Character calculator set
		if (oldSkill != null) {
			//this is just a fail-safe againts buggers and gm dummies...
			if ((oldSkill.triggerAnotherSkill()) && oldSkill.getTriggeredId() > 0) {
				removeSkill(oldSkill.getTriggeredId(), true);
			}

			if (cancelEffect) {
				// for now, to support transformations, we have to let their
				// effects stay when skill is removed
				L2Effect e = getFirstEffect(oldSkill);
				if (e == null || e.getEffectType() != L2EffectType.TRANSFORMATION) {
					removeStatsOwner(oldSkill);
					stopSkillEffects(oldSkill.getId());
				}
			}

			if (oldSkill instanceof L2SkillAgathion && this instanceof FuzePcInstance) {
				((FuzePcInstance) this).broadcastUserInfo();
			}

			if (oldSkill instanceof L2SkillMount && this instanceof FuzePcInstance && ((FuzePcInstance) this).isMounted())
				((FuzePcInstance) this).dismount();

			if (oldSkill.isChance() && _chanceSkills != null) {
				removeChanceSkill(oldSkill.getId());
			}
		}

		return oldSkill;
	}

	public synchronized void addChanceSkill(L2Skill skill) {
		if (_chanceSkills == null)
			_chanceSkills = new ChanceSkillList(this);

		_chanceSkills.put(skill, skill.getChanceCondition());
	}

	public synchronized void removeChanceSkill(int id) {
		if (_chanceSkills == null)
			return;
		for (IChanceSkillTrigger trigger : _chanceSkills.keySet()) {
			if (!(trigger instanceof L2Skill))
				continue;

			L2Skill skill = (L2Skill) trigger;

			if (skill.getId() == id)
				_chanceSkills.remove(skill);
		}

		if (_chanceSkills.isEmpty())
			_chanceSkills = null;
	}

	public synchronized void addChanceEffect(EffectChanceSkillTrigger effect) {
		if (_chanceSkills == null)
			_chanceSkills = new ChanceSkillList(this);

		_chanceSkills.put(effect, effect.getTriggeredChanceCondition());
	}

	public synchronized void removeChanceEffect(EffectChanceSkillTrigger effect) {
		if (_chanceSkills == null)
			return;

		_chanceSkills.remove(effect);

		if (_chanceSkills.isEmpty())
			_chanceSkills = null;
	}

	/**
	 * Return all skills own by the L2Character in a table of L2Skill.<BR>
	 * <BR>
	 * 
	 * <B><U> Concept</U> :</B><BR>
	 * <BR>
	 * All skills own by a L2Character are identified in <B>_skills</B> the L2Character <BR>
	 * <BR>
	 * 
	 */
	public final L2Skill[] getAllSkills() {
		if (_skills == null)
			return new L2Skill[0];

		return _skills.values().toArray(new L2Skill[_skills.values().size()]);
	}

	public ChanceSkillList getChanceSkills() {
		return _chanceSkills;
	}

	/**
	 * Return the level of a skill owned by the L2Character.<BR>
	 * <BR>
	 * 
	 * @param skillId
	 *            The identifier of the L2Skill whose level must be returned
	 * 
	 * @return The level of the L2Skill identified by skillId
	 * 
	 */
	public int getSkillLevel(int skillId) {
		final L2Skill skill = getKnownSkill(skillId);
		if (skill == null)
			return -1;

		return skill.getLevel();
	}

	/**
	 * 玩家是否有这个技能
	 * 
	 * @param sk
	 * @return
	 */
	public boolean hasSkill(L2Skill sk) {
		if (_skills == null)
			return false;

		return _skills.containsValue(sk);
	}

	/**
	 * Return True if the skill is known by the L2Character.<BR>
	 * <BR>
	 * 
	 * @param skillId
	 *            The identifier of the L2Skill to check the knowledge
	 * 
	 */
	public final L2Skill getKnownSkill(int skillId) {
		if (_skills == null)
			return null;

		return _skills.get(skillId);
	}

	/** Task lauching the function enableSkill() */
	class EnableSkill implements Runnable {
		int _skillId;

		public EnableSkill(int skillId) {
			_skillId = skillId;
		}

		public void run() {
			try {
				enableSkill(_skillId);
			} catch (Exception e) {
				_log.error("Failed executing enableSkill().", getObjectId(), e);
			}
		}
	}

	/**
	 * Enable a skill (remove it from _disabledSkills of the L2Character).<BR>
	 * <BR>
	 * 
	 * <B><U> Concept</U> :</B><BR>
	 * <BR>
	 * All skills disabled are identified by their skillId in <B>_disabledSkills</B> of the L2Character <BR>
	 * <BR>
	 * 
	 * @param skillId
	 *            The identifier of the L2Skill to enable
	 * 
	 */
	public void enableSkill(int skillId) {
		if (_disabledSkills == null)
			return;

		_disabledSkills.remove(Integer.valueOf(skillId));

		if (this instanceof FuzePcInstance)
			removeTimeStamp(skillId);
	}

	/**
	 * Disable a skill (add it to _disabledSkills of the L2Character).<BR>
	 * <BR>
	 * 
	 * <B><U> Concept</U> :</B><BR>
	 * <BR>
	 * All skills disabled are identified by their skillId in <B>_disabledSkills</B> of the L2Character <BR>
	 * <BR>
	 * 
	 * @param skillId
	 *            The identifier of the L2Skill to disable
	 * 
	 */
	public void disableSkill(int skillId) {
		if (_disabledSkills == null)
			_disabledSkills = Collections.synchronizedList(new FastList<Integer>());

		_disabledSkills.add(skillId);
	}

	/**
	 * Disable this skill id for the duration of the delay in milliseconds.
	 * 
	 * @param skillId
	 * @param delay
	 *            (seconds * 1000)
	 */
	public void disableSkill(int skillId, int delay) {
		disableSkill(skillId);
		new Timer(delay, false, new EnableSkill(skillId)).start();
		//		if (delay > 10) {
		//			new Timer(delay, false, new EnableSkill(skillId)).start();
		//		}
	}

	/**
	 * Check if a skill is disabled.<BR>
	 * <BR>
	 * 
	 * <B><U> Concept</U> :</B><BR>
	 * <BR>
	 * All skills disabled are identified by their skillId in <B>_disabledSkills</B> of the L2Character <BR>
	 * <BR>
	 * 
	 * @param skillId
	 *            The identifier of the L2Skill to disable
	 * 
	 */
	public boolean isSkillDisabled(int skillId) {
		if (isAllSkillsDisabled())
			return true;

		if (_disabledSkills == null)
			return false;

		return _disabledSkills.contains(skillId);
	}

	public final void setSkillCast(Timer newSkillCast) {
		_skillCast = newSkillCast;
	}

	public void setPvpFlagLasts(long time) {
		_pvpFlagLasts = time;
	}

	public long getPvpFlagLasts() {
		return _pvpFlagLasts;
	}

	public void startPvPFlag() {
		updatePvPFlag(1);
		_PvPRegTask = new Timer(1000, true, new PvPFlag());
	}

	public void stopPvpRegTask() {
		if (_PvPRegTask != null)
			_PvPRegTask.cancel();
	}

	public void stopPvPFlag() {
		stopPvpRegTask();
		updatePvPFlag(0);
		_PvPRegTask = null;
	}

	public void updatePvPFlag(int value) {
		// Overridden in L2PcInstance
	}

	/**
	 * Return a Random Damage in function of the weapon.<BR>
	 * <BR>
	 */
	public final int getRandomDamage(L2Character target) {
		L2Weapon weaponItem = getActiveWeaponItem();

		if (weaponItem == null)
			return 5 + (int) Math.sqrt(getLevel());

		//		return weaponItem.getRandomDamage();
		return 1;
	}

	@Override
	public String toString() {
		return "mob " + getObjectId();
	}

	public int getAttackEndTime() {
		return _attackEndTime;
	}

	/**
	 * Not Implemented.<BR>
	 * <BR>
	 */
	public abstract int getLevel();

	public final float calcStat(Stats stat, int init, L2Character target, L2Skill skill) {
		return getStat().calcStat(stat, init, target, skill);
	}

	// Property - Public
	/**
	 * 获得角色的命中率
	 */
	public int getAccuracy() {
		return getStat().getAccuracy();
	}

	/**
	 * 获得角色的体制
	 * 
	 * @return
	 */
	public int getCON() {
		return getStat().getCON();
	}

	/**
	 * 获得角色的敏捷
	 * 
	 * @return
	 */
	public int getDEX() {
		return getStat().getDEX();
	}

	/**
	 * 获得角色的力量
	 * 
	 * @return
	 */
	public int getSTR() {
		return getStat().getSTR();
	}

	/**
	 * 获得角色的智力
	 * 
	 * @return
	 */
	public int getINT() {
		return getStat().getINT();
	}

	/**
	 * 获得角色的暴击率
	 * 
	 * @param skill
	 * @return
	 */
	public int getCriticalHit() {
		return getStat().getCriticalHit();
	}

	/**
	 * 获得角色的闪避率
	 * 
	 * @return
	 */
	public int getEvasionRate() {
		return getStat().getEvasionRate();
	}

	public final int getMagicalAttackRange(L2Skill skill) {
		return getStat().getMagicalAttackRange(skill);
	}

	public int getMAtk() {
		return getStat().getMAtk();
	}

	public int getMaxMp() {
		return getStat().getMaxMp();
	}

	public int getMaxHp() {
		return getStat().getMaxHp();
	}

	public int getMDef() {
		return getStat().getMDef();
	}

	public int getMEN() {
		return getStat().getMEN();
	}

	public double getMReuseRate(L2Skill skill) {
		return getStat().getMReuseRate(skill);
	}

	public int getPAtk() {
		return getStat().getPAtk();
	}

	public int getPAtkSpd() {
		return getStat().getPAtkSpd();
	}

	public int getPDef() {
		return getStat().getPDef();
	}

	public final int getPhysicalAttackRange() {
		return getStat().getPhysicalAttackRange();
	}

	public int getRunSpeed() {
		return getStat().getRunSpeed();
	}

	public int getMinPAtk() {
		return getStat().getMinPAtk();
	}

	public int getMaxPAtk() {
		return getStat().getMaxPAtk();
	}

	public int getMinMAtk() {
		return getStat().getMinMAtk();
	}

	public int getMaxMAtk() {
		return getStat().getMaxMAtk();
	}

	public int getBleed() {
		return getStat().getBleed();
	}

	public int getPosion() {
		return getStat().getPosion();
	}

	public int getStun() {
		return getStat().getStun();
	}

	public int getRoot() {
		return getStat().getRoot();
	}

	public int getMovement() {
		return getStat().getMovement();
	}

	public int getConfusion() {
		return getStat().getConfusion();
	}

	public int getSleep() {
		return getStat().getSleep();
	}

	public int getBleedVuln() {
		return getStat().getBleedVuln();
	}

	public int getPoisonVuln() {
		return getStat().getPoisonVuln();
	}

	public int getStunVuln() {
		return getStat().getStunVuln();
	}

	public int getRootVuln() {
		return getStat().getRootVuln();
	}

	public int getMovementVuln() {
		return getStat().getMovementVuln();
	}

	public int getConfusionVuln() {
		return getStat().getConfusionVuln();
	}

	public int getSleepVuln() {
		return getStat().getSleepVuln();
	}

	// =========================================================

	// =========================================================
	// Status - NEED TO REMOVE ONCE L2CHARTATUS IS COMPLETE
	// Method - Public
	public void addStatusListener(L2Character object) {
		getStatus().addStatusListener(object);
	}

	public void reduceCurrentHp(int i, L2Character attacker, L2Skill skill) {
		reduceCurrentHp(i, attacker, true, false, skill);
	}

	public void reduceCurrentHpByDOT(int i, L2Character attacker, L2Skill skill) {
		reduceCurrentHp(i, attacker, true, true, skill);
	}

	public void reduceCurrentHp(int i, L2Character attacker, boolean awake, boolean isDOT, L2Skill skill) {
		getStatus().reduceHp(i, attacker, awake, isDOT, false);
	}

	/**
	 * 从角色的状态通知监听池里删除某个对象
	 * 
	 * @param object
	 */
	public void removeStatusListener(L2Character object) {
		getStatus().removeStatusListener(object);
	}

	/**
	 * 减少角色的MP
	 * 
	 * @param i
	 *            减少的值
	 */
	public void reduceCurrentMp(int i) {
		getStatus().reduceMp(i);
	}

	/**
	 * 获得当前的HP
	 * 
	 * @return
	 */
	public final int getCurrentHp() {
		return getStatus().getCurrentHp();
	}

	/**
	 * 获得当前 Hp 比率 %比
	 * 
	 * @return
	 */
	public final int getCurrentHpRadio() {
		int radio = (getCurrentHp() * 100) / getMaxHp();
		return radio > 100 ? 100 : radio;
	}

	/**
	 * 获得当前 Mp 比率 %比
	 * 
	 * @return
	 */
	public final int getCurrentMpRadio() {
		int radio = (getCurrentMp() * 100) / getMaxMp();
		return radio > 100 ? 100 : radio;
	}

	/**
	 * 设置当前的HP
	 * 
	 * @param newHp
	 */
	public final void setCurrentHp(int newHp) {
		getStatus().setCurrentHp(newHp);
	}

	/**
	 * 设置当前的HP/MP
	 * 
	 * @param newHp
	 * @param newMp
	 */
	public final void setCurrentHpMp(int newHp, int newMp) {
		getStatus().setCurrentHpMp(newHp, newMp);
	}

	/**
	 * 获得当前的MP
	 * 
	 * @return
	 */
	public final int getCurrentMp() {
		return getStatus().getCurrentMp();
	}

	/**
	 * 设置当前的MP
	 * 
	 * @param newMp
	 */
	public final void setCurrentMp(int newMp) {
		getStatus().setCurrentMp(newMp);
	}

	// =========================================================

	public L2Character getLastBuffer() {
		return _lastBuffer;
	}

	public int getLastHealAmount() {
		return _lastHealAmount;
	}

	public void setLastBuffer(L2Character buffer) {
		_lastBuffer = buffer;
	}

	public void setLastHealAmount(int hp) {
		_lastHealAmount = hp;
	}

	/**
	 * Check player max buff count
	 * 
	 * @return max buff count
	 */
	public int getMaxBuffCount() {
		return Config.BUFFS_MAX_AMOUNT;
	}

	public final void startPhysicalAttackMuted() {
		setIsPhysicalAttackMuted(true);
	}

	public final void stopPhysicalAttackMuted(L2Effect effect) {
		if (effect == null)
			stopEffects(L2EffectType.PHYSICAL_ATTACK_MUTE);
		else
			removeEffect(effect);

		setIsPhysicalAttackMuted(false);
	}

	/**
	 * 返回是否是团队型NPC（boss或他的仆从）
	 * 
	 * @return
	 */
	public boolean isRaid() {
		return _isRaid;
	}

	/**
	 * 设置是团队型NPC
	 * 
	 * @param isRaid
	 */
	public void setIsRaid(boolean isRaid) {
		_isRaid = isRaid;
	}

	/**
	 * 返回是否是团队型NPC中的仆从
	 * 
	 * @return
	 */
	public boolean isRaidMinion() {
		return _isMinion;
	}

	/**
	 * 设置是团队型NPC中的仆从
	 * 
	 * @param isRaid
	 */
	public void setIsRaidMinion(boolean val) {
		_isRaid = val;
		_isMinion = val;
	}

	/**
	 * 初始化 角色身上的 Hp,Mp,Exp的 更新
	 */
	public void reInitCharStatusUpdate() {
		getStatus().initCharStatusUpdateValues();
	}
}
